cf round 480E The Number Games

时间:2024-04-11 10:35:05

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