Atcoder Regular Contest 125 F - Tree Degree Subset Sum(发掘性质+单调队列)

很显然的,题目要我们求的是个 01 背包,一看数据范围,\(2\times 10^5\),GG。

考虑挖掘一些性质。令 \(d_i=deg_i-1\)\(L_s\) 表示最少需要多少个点才能使得它们的 \(d\) 之和恰好等于 \(s\)\(R_s\) 表示最多需要多少个点才能使得它们的 \(d\) 之和恰好等于 \(s\)。我们断言,\(\forall c\in[L_s,R_s]\),总存在一个大小为 \(c\) 的子集满足其 \(d\) 之和恰好等于 \(s\)

考虑证明,设 \(\sum[d_i=0]=x\),那么如果我们能证明 \(R_s-L_s\le 2x\) 那么该命题就成立了,因为 \(L_s\) 对应的方案中必然不存在 \(d_i=0\) 的元素,\(R_s\) 对应的方案中必然存在所有 \(x\)\(d_i=0\) 的元素。而对于任意子集,设 \(S,C\) 分别表示子集中 \(d\) 之和与子集大小,那么有 \(-x\le S-C\le x-2\),这是因为 \(\sum[d_i-1<0](d_i-1)=-x,\sum[d_i-1>0](d_i-1)=x-2\)。故 \(-x\le s-R_s\le s-L_s\le x-2\),故 \(R_s-L_s\le 2x\)

有了这个性质之后就好做了,由于 \(\sum d_i=O(n)\),根据经典性质,\(d\) 中数的种类数是 \(O(\sqrt{n})\) 的,对于每种数拉出来跑一遍单调队列即可求出 \(L,R\),时间复杂度 \(n\sqrt{n}\)

const int MAXN=2e5;
const int INF=0x3f3f3f3f;
int n,deg[MAXN+5],cnt[MAXN+5],mx[MAXN+5],mn[MAXN+5];
void ins(int W,int V){for(int i=n;i>=W;i--)chkmin(mn[i],mn[i-W]+V),chkmax(mx[i],mx[i-W]+V);}
int main(){
#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d",&n);for(int i=1;i<=n;i++)deg[i]=-1;
	for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),deg[u]++,deg[v]++;
	for(int i=1;i<=n;i++)cnt[deg[i]]++;
	memset(mx,192,sizeof(mx));memset(mn,63,sizeof(mn));mx[0]=mn[0]=0;
	for(int i=0;i<=n;i++)if(cnt[i]){
		int cur=0;while((1<<cur+1)<=cnt[i])++cur;
		for(int j=0;j<cur;j++)ins((1<<j)*i,(1<<j));
		ins((cnt[i]-((1<<cur)-1))*i,cnt[i]-((1<<cur)-1));
	}ll res=0;
	for(int i=0;i<=n;i++)if(mn[i]<INF)res+=mx[i]-mn[i]+1;
	printf("%lld\n",res);
	return 0;
}
posted @ 2022-12-21 11:54  tzc_wk  阅读(31)  评论(0)    收藏  举报