cf round 480E The Number Games

题意:给一棵树,点$i$的点权是$2^i$,你需要删掉$k$个点,使得剩下的点连通的情况下剩下的点权值和最大。

$k \leq n \leq 10^6$

如果考虑删哪些点,是不好考虑的,会出问题。

反过来考虑,要保留哪些点,这样我们才可以保证让保留的点权值和最大。

我们以$n$这个点为根,然后从$n$到$1$依次考虑这个点可不可以保留,能保留就保留。

一个点能保留的条件是:因为它要保留,而需要保留的所有点的个数+现在已确定保留的点的个数$\leq n-k$

哪些是“因为它要保留,而需要保留的点”呢,就是它到根的路径上没有确定保留的点

就是如果它要保留,它到根的路径上的点都要保留,这样才能连通。

这个可以用树上倍增,一个log搞定

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=1e6+7,maxt=23,W=21;
int n,k;
bool init[maxn];

char cc; ll ff;
template<typename T>void read(T& aa) {
	aa=0;cc=getchar();ff=1;
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

int fir[maxn],nxt[2*maxn],to[2*maxn],e=0;
void add(int x,int y) {
	to[++e]=y;nxt[e]=fir[x];fir[x]=e;
	to[++e]=x;nxt[e]=fir[y];fir[y]=e;
}

int fa[maxn][maxt],dep[maxn];
void s(int pos,int f) {
	fa[pos][0]=f; dep[pos]=dep[f]+1;
	For(i,1,W) fa[pos][i]=fa[fa[pos][i-1]][i-1];
	int y,z;
	for(y=fir[pos];y;y=nxt[y]) {
		if((z=to[y])==f) continue;
		s(z,pos);
	}
}

int find(int x) {
	Rep(i,W,0) if(!init[fa[x][i]]) x=fa[x][i];
	return x;
}

int main() {
	read(n); read(k); k=n-k;
	int x,y; init[0]=1;
	For(i,1,n-1) {
		read(x); read(y);
		add(x,y);
	}
	s(n,0);
	Rep(i,n,1) {
		if(init[i]) continue; x=find(i);
		if(dep[i]-dep[x]+1<=k) {
			k-=(dep[i]-dep[x]+1);
			for(y=i;!init[y];y=fa[y][0]) init[y]=1;
		}
	}
	For(i,1,n) if(!init[i]) printf("%d ",i);
	printf("\n");
	return 0;
}

 

  

 

posted @ 2018-05-27 10:07  shixinyi  阅读(203)  评论(0编辑  收藏  举报