1. 程式人生 > >CF980E The Number Games【樹鏈剖分/線段樹】

CF980E The Number Games【樹鏈剖分/線段樹】

CF980E The Number Games

題意翻譯

Panel 國將舉辦名為數字遊戲的年度表演。每個省派出一名選手。

國家有 n 個編號從 1 到 n 的省,每個省剛好有一條路徑將其與其他省相連。第 i 個省出來的代表有 2i 名粉絲。

今年,主席打算削減開支,他想要踢掉 k 個選手。但是,被踢掉的選手的省將很 angry 並且不會讓別的任何人從這個省經過。

主席想確保所有剩下選手的省都互相可達,他也希望最大化參與表演的選手的粉絲數。

主席該踢掉哪些選手呢?

輸入格式

輸入n,k 。( k<n106 )

下來是 n1 行,一行兩個數,代表一條道路的起終點。

輸出格式

升序輸出要踢掉的選手編號。

感謝@poorpool 提供的翻譯

題目描述

The nation of Panel holds an annual show called The Number Games, where each district in the nation will be represented by one contestant.

The nation has nn districts numbered from 11 to nn , each district has exactly one path connecting it to every other district. The number of fans of a contestant from district ii is equal to 2^i2i .

This year, the president decided to reduce the costs. He wants to remove kk contestants from the games. However, the districts of the removed contestants will be furious and will not allow anyone to cross through their districts.

The president wants to ensure that all remaining contestants are from districts that can be reached from one another. He also wishes to maximize the total number of fans of the participating contestants.

Which contestants should the president remove?

輸入輸出格式

輸入格式:

 

The first line of input contains two integers n and k ( 1k<n106 ) — the number of districts in Panel, and the number of contestants the president wishes to remove, respectively.

The next n1 lines each contains two integers aa and b ( 1a,bn , ab ), that describe a road that connects two different districts a and b in the nation. It is guaranteed that there is exactly one path between every two districts.

 

輸出格式:

 

Print k space-separated integers: the numbers of the districts of which the contestants should be removed, in increasing order of district number.

 

輸入輸出樣例

輸入樣例#1: 複製
6 3
2 1
2 6
4 2
5 6
2 3
輸出樣例#1: 複製
1 3 4
輸入樣例#2: 複製
8 4
2 6
2 7
7 8
1 2
3 1
2 4
7 5
輸出樣例#2: 複製
1 3 4 5

說明

In the first sample, the maximum possible total number of fans is 2^2 + 2^5 + 2^6 = 100 . We can achieve it by removing the contestants of the districts 1, 3, and 4.


Solution

哭,寫完才發現為什麼別人的程式碼那麼短QAQ

暴拍了個樹剖+線段樹修改QAQ

思路也就是貪心了,把$n$定為根節點,從大往小選,要選它選意味著這個點到根節點的點全都要選,所以每次計算這條路上可以選的數量,能選就選,然後修改線段樹,最後查詢線段樹上沒有被修改的就行了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n, k;

struct Node {
    int u, v, nex;
    Node(int u = 0, int v = 0, int nex = 0) :
        u(u), v(v), nex(nex) { }
} Edge[2000005];

int h[1000005], stot;
void add(int u, int v) {
    Edge[++stot] = Node(u, v, h[u]);
    h[u] = stot;
}

int fa[1000005], siz[1000005], son[1000005];
void dfs1(int u, int f) {
    fa[u] = f;    siz[u] = 1;
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == f)    continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]])    son[u] = v;
    }
}

int top[1000005], in[1000005], idc;
void dfs2(int u, int t) {
    top[u] = t;    in[u] = ++idc;
    if(son[u])    dfs2(son[u], t);
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(v == fa[u] || v == son[u])    continue;
        dfs2(v, v);
    }
}

int TR[4000005], tag[4000005];
void update(int nd) {
    TR[nd] = TR[nd << 1] + TR[nd << 1 | 1];
}

void push_down(int nd, int l, int r) {
    if(~tag[nd]) {
        int mid = (l + r) >> 1;
        TR[nd << 1] = 0;
        TR[nd << 1 | 1] = 0;
        tag[nd << 1] = 0; tag[nd << 1 | 1] = 0;
        tag[nd] = -1;
    }
}

void build(int nd, int l, int r) {
    tag[nd] = -1;
    if(l == r) {
        TR[nd] = 1;
        return ;
    }
    int mid = (l + r) >> 1;
    build(nd << 1, l, mid);
    build(nd << 1 | 1, mid + 1, r);
    update(nd);
}

int query(int nd, int l, int r, int L, int R) {
    if(l >= L && r <= R)    return TR[nd];
    push_down(nd, l, r);
    int ans = 0; int mid = (l + r) >> 1;
    if(L <= mid)    ans += query(nd << 1, l, mid, L, R);
    if(R > mid)        ans += query(nd << 1 | 1, mid + 1, r, L, R);
    return ans;
}

void modify(int nd, int l, int r, int L, int R) {
    if(l >= L && r <= R) {
        TR[nd] = 0;
        tag[nd] = 0;
        return ;
    }
    push_down(nd, l, r);
    int mid = (l + r) >> 1;
    if(L <= mid)    modify(nd << 1, l, mid, L, R);
    if(R > mid)        modify(nd << 1 | 1, mid + 1, r, L, R);
    update(nd);
}

int query(int u) {
    int ans = 0;
    while(top[u] != 0) {
        ans += query(1, 1, n, in[top[u]], in[u]);
        u = fa[top[u]];
    }
    ans += query(1, 1, n, in[n], in[u]);
    return ans;
}

void modify(int u) {
    while(top[u] != 0) {
        modify(1, 1, n, in[top[u]], in[u]);
        u = fa[top[u]];
    }
    modify(1, 1, n, in[n], in[u]);
}

int use[1000005];
int main() {
    scanf("%d%d", &n, &k);
    memset(use, 1, sizeof(use));
    for(int i = 1; i < n; i ++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v); add(v, u);
    }
    dfs1(n, 0); dfs2(n, 0);
    build(1, 1, n);
    int tot = n - k;
    for(int i = n; i >= 1 && tot; i --) {
        int tmp = query(i);
        if(tmp <= tot) {
            tot -= tmp;
            modify(i);
        }
    }
    for(int i = 1; i <= n; i ++)
        if(query(1, 1, n, in[i], in[i]))    printf("%d ", i);
    return 0;
}