980E. The Number Games(倍增,思維)

980E. The Number Games(倍增,思維)

題目鏈接:傳送門

思路:

​ 我們轉化爲,從一顆樹上選n-k個點,使得貢獻最大,且這n-k個點兩兩連通。貪心的取,我們必定先取大的(因爲如果可以取大的但不取必虧)。

​ 我們可以將原圖變爲以n爲根的有根樹,首先n號點必選,我們接下來探討下面選點,我們建立倍增數組,fa[u][i]fa[u][i]代表u的第2i2^i個祖先的編號,我們看編號爲n1n-1的點。

如果編號爲n1n-1到編號爲 nn 的點的數量小於剩下可選的點的數量,那就不能選。

否則,我們選n1n-1號點,爲了連通性, n1n-1號點到nn號點之間的點都要選。

我們將選的點進行標記。

所以我們有一個算法:

先選編號爲n的點,然後遍歷剩下編號爲的點(從大到小),假設此時編號爲ii,如果編號爲ii的點已經被選,則跳過,否則倍增找祖先沒有被標記的最小的近的祖先,(爲什麼是祖先呢?,因爲被標記的不可能是 i 的兒子,否則他就被選了)那麼找最近被標記的點就可以用倍增。

如果之間點的數量小於可選的點的數量,就不選。否則就選,並選其之間的點。

#include<bits/stdc++.h>
#define mse(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=1e6+10;
vector<int> g[N];
int book[N];
int fa[N][20];
void bfs(int root)
{
    queue<int> qe;
    qe.push(root);
    fa[root][0]=root;
    while(!qe.empty())
    {
        int u=qe.front();qe.pop();
        for(int i=1;i<20;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int v:g[u])
        {
            if(v==fa[u][0]) continue;
            fa[v][0]=u;
            qe.push(v);
        }
    }
}
int mincost(int v)
{
    int ans=0;
    if(book[fa[v][0]]) return 1;
    int wv=v;
    for(int i=19;i>=0;--i)
    {
        if(book[fa[wv][i]]) continue;
        ans+=1<<i;
        wv=fa[wv][i];
    }
    return ans+1;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    cout.tie(0);
    int n,k;
    cin>>n>>k;
    for(int i = 1; i < n; ++i)
    {
        int u,v;
        cin>>u>>v;;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    k=n-k-1;
    bfs(n);
    book[n]=1;
    for(int i=n-1;i>=1&&k;--i)
    {
        if(book[i]) continue;
        int m=mincost(i);
        if(m<=k)
        {
            k-=m;
            int wv=i;
            while(!book[wv]){
                book[wv]=1;
                wv=fa[wv][0];
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(!book[i])
            cout<<i<<" ";
    }
    cout<<endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章