题意:给一棵树,点$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(" "); return 0; }