• [ARC125F] Tree Degree Subset Sum


    零、前言

    自闭场,( t D) 想复杂白给,结束前 (10) 分钟想出正确做法忘写树状数组( t C,E) 赛后随便切 (...)

    不要硬刚一道题,不要完全看过题人数来决定你做哪道题,( t think twice,code once)

    ( t zxy) 掉大分》正在连载中 (...)

    一、题目

    点此看题

    给定 (n) 个点的树,每个点有一个度数 (d_i),问有多少个满足下列条件的二元组 ((x,y))

    • (0leq xleq n),并且存在选出 (x) 个点的方式使得其度数和为 (y)

    (nleq 2cdot 10^5)

    二、解法

    首先我声明树这个背景没有任何卵用,根据 ( t purfer) 序列的理论,任何一种度数序列都能对应至少一棵树,问题又只和度数序列有关,既然树对度数序列没有任何限制那么可以不管他。

    我们把所有 (d_i) 减少 (1),那么问题变成了 (n) 个物品体积总和为 (n-2) 的背包问题,最烦的是要记录选了多少个物品。

    Lamma:设 (L(x)) 表示总体积为 (x) 的最少物品数,(R(x)) 表示总体积为 (x) 的最多物品数,那么对于选出 (t) 个物品总和为 (x),当且仅当 (L(x)leq tleq R(x)) 是可以实现的。

    证明:必要性易得,下证充分性。

    设体积为 (0) 的物品一共有 (z) 个,可以转证下列问题:

    [R(x)-L(x)leq 2z+1 ]

    这是因为 (R(x)) 通过去掉体积为 (0) 的物品可以得到 (tin[R(x)-z,R(x)]) 的方案,(L(x)) 可以通过增加体积为 (0) 的物品得到 (tin[L(x),L(x)+z]) 的方案。

    考虑任意一种背包方案权值和为 (k),数量为 (c),那么有:

    [-zleq k-cleq z-2 ]

    因为让 (k-c) 最小直接选那些体积为 (0) 的物品,让 (k-c) 最大需要选体积大于 (0) 的物品:

    [k-c=sum d_i-sum [d_i> 0]=n-2-(n-z)=z-2 ]

    那么 (k-L(x))(k-R(x)) 一定在 ([-z,z-2]) 这个范围里,所以 (R(x)-L(x)) 不超过 (2z-2),证毕。

    只需要用 (dp) 求出 (L(x),R(x)) 即可,因为物品种类数不超过 (sqrt n),对于每一种物品,可以把体积按余数分类,对于每一种余数跑单调队列即可,时间复杂度 (O(nsqrt n))

    三、总结

    首先排除不必要的背景,比如本题树的背景对原问题没有任何限制那么是可以排除的。

    对于难做的点,找必要条件猜结论。

    最后物品种类数很少的背包问题,每种物品分别处理,按余数分类之后解决。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 200005;
    #define ll long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,z,c[M],d[M],p[M],mi[M],mx[M],dp[M];ll ans;
    signed main()
    {
    	n=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		d[u]++;d[v]++;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		d[i]--,c[d[i]]++;
    		mi[i]=1e9,mx[i]=-1e9;
    		if(!d[i]) z++;
    	}
    	mi[0]=mx[0]=0;
    	//cal mx
    	for(int i=1;i<=n;i++)
    	{
    		int x=c[i];
    		if(!x) continue;
    		for(int j=0;j<=n;j++) dp[j]=mx[j]; 
    		for(int j=0;j<i;j++)
    		{
    			int l=1,r=0,nw=j;
    			for(int k=0;nw<=n;k++,nw+=i)
    			{
    				while(l<=r && p[l]<nw-i*x) l++;
    				if(l<=r) mx[nw]=max(mx[nw],dp[p[l]]+k-p[l]/i);
    				while(l<=r && dp[p[r]]-p[r]/i<=dp[nw]-nw/i) r--;
    				p[++r]=nw;
    			}
    		}
    	}
    	//cal mi
    	for(int i=1;i<=n;i++)
    	{
    		int x=c[i];
    		if(!x) continue;
    		for(int j=0;j<=n;j++) dp[j]=mi[j]; 
    		for(int j=0;j<i;j++)
    		{
    			int l=1,r=0,nw=j;
    			for(int k=0;nw<=n;k++,nw+=i)
    			{
    				while(l<=r && p[l]<nw-i*x) l++;
    				if(l<=r) mi[nw]=min(mi[nw],dp[p[l]]+k-p[l]/i);
    				while(l<=r && dp[p[r]]-p[r]/i>=dp[nw]-nw/i) r--;
    				p[++r]=nw;
    			}
    		}
    	}
    	//cal answer
    	for(int i=0;i<=n;i++)
    		if(mi[i]<=mx[i])
    			ans+=mx[i]-mi[i]+1+z;
    	printf("%lld
    ",ans);
    }
    
  • 相关阅读:
    React antd如何实现<Upload>组件上传附件再次上传已清除附件缓存问题。
    spring项目logback日志与logstash和Elasticsearch整合
    Java后端面试经验总结分享(一)
    【设计模式】访问者模式
    【设计模式】命令模式
    【设计模式】模板方法模式
    【设计模式】代理模式
    【设计模式】享元模式
    【设计模式】外观模式
    【设计模式】组合模式
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15175343.html
Copyright © 2020-2023  润新知