[ARC125F]Tree Degree Subset Sum

Tree Degree Subset Sum

题解

我们考虑记 d i d_{i} di为点 i i i的度数减 1 1 1,此时有 ∑ i = 1 n d i = n − 2 \sum_{i=1}^{n}d_{i}=n-2 i=1ndi=n2
很明显,此时不同的 d d d最多有 n \sqrt{n} n 个。
我们记 m ( s ) m\left(s\right) m(s) d d d的总和为 s s s时最少的选择点集合,而 M ( s ) M\left(s\right) M(s)表示 d d d总和为 s s s时所选择的点的最多的集合, A ( s ) A(s) A(s)表示何为 s s s的某一个选择点集合。
我们记 c n t i cnt_{i} cnti表示 d d d i i i的点的个数,很容易发现有 ∑ i ⩾ 2 c n t i ⩽ c n t 0 − 2 \sum_{i\geqslant 2}cnt_{i}\leqslant cnt_{0}-2 i2cnticnt02
可得 − c n t 0 ⩽ s − ∣ M ( s ) ∣ ⩽ s − ∣ m ( s ) ∣ ⩽ ∑ i ⩾ 2 c n t i = c n t 0 − 2 -cnt_{0}\leqslant s-|M(s)|\leqslant s-|m(s)|\leqslant \sum_{i\geqslant 2}cnt_{i}=cnt_{0}-2 cnt0sM(s)sm(s)i2cnti=cnt02,相当于从 M ( s ) M(s) M(s)的集合中找出所有非 1 1 1的点。
自然有,
( s − ∣ m ( s ) ∣ ) − ( s − ∣ M ( s ) ∣ ) = ∣ M ( s ) ∣ − ∣ m ( s ) ∣ ⩽ c n t 0 − 2 + c n t 0 = 2 c n t 0 − 2 (s-|m(s)|)-(s-|M(s)|)=|M(s)|-|m(s)|\leqslant cnt_{0}-2+cnt_{0}=2cnt_{0}-2 (sm(s))(sM(s))=M(s)m(s)cnt02+cnt0=2cnt02
而集合 M ( s ) M(s) M(s)中肯定包含所有 0 0 0,而集合 m ( s ) m(s) m(s)中肯定一个 0 0 0都不包含,在其中加入 0 0 0与取出 0 0 0可以实现 [ ∣ m ( s ) ∣ , ∣ m ( s ) ∣ + c n t 0 ] ⋃ [ ∣ M ( s ) ∣ − c n t 0 , ∣ M ( s ) ∣ ] = [ ∣ m ( s ) ∣ , ∣ M ( s ) ∣ ] [|m(s)|,|m(s)|+cnt_{0}]\bigcup[|M(s)|-cnt_{0},|M(s)|]=[|m(s)|,|M(s)|] [m(s),m(s)+cnt0][M(s)cnt0,M(s)]=[m(s),M(s)],所以我们选择构成 s s s的数的个数在 [ ∣ m ( s ) ∣ , ∣ M ( s ) ∣ ] [|m(s)|,|M(s)|] [m(s),M(s)]之间都是可以的。
而每一个 ( x , y ) (x,y) (x,y)肯定都对应唯一的 ( x , y − x ) (x,y-x) (x,yx),所以我们统计出的每一个 ( s , ∣ A ( s ) ∣ ) (s,|A(s)|) (s,A(s))都是都与某一个 ( x , y ) (x,y) (x,y)一一映射。

既然我们不同的 d d d最多有 n \sqrt{n} n 个,我们可以考虑用 d p dp dp来求出 ∣ m ( s ) ∣ |m(s)| m(s) ∣ M ( s ) ∣ |M(s)| M(s)
我们定义 d p i dp_{i} dpi表示 ∣ m ( i ) ∣ |m(i)| m(i),转移明显是一个背包的过程。
对于 d d d相同的数,我们可以采取优化更快的方法进行转移。
虽然二进制拆分可以做到 O ( n n log ⁡   n ) O\left(n\sqrt{n}\log\,n\right) O(nn logn),我们仍然可以优化复杂度。
考虑将对于 i ∈ [ 0 , n − 2 ] i\in[0,n-2] i[0,n2]我们可以将它们按模 d d d的值分为 d d d类, d p i dp_{i} dpi可以从 { d p x ∣ x ∈ [ i − d s u m d , i ] ∧ x ≡ i ( m o d   d ) } \{dp_{x}|x\in[i-dsum_{d},i]\wedge x\equiv i(mod\,d)\} {dpxx[idsumd,i]xi(modd)}转移过来,可以用单调队列进行优化。
这样,我们的 d p dp dp转移就被成功优化到 O ( n n ) O\left(n\sqrt{n}\right) O(nn )了。
显然, ∣ M ( i ) ∣ = n − ∣ m ( n − 2 − i ) ∣ |M(i)|=n-|m(n-2-i)| M(i)=nm(n2i),由于 ∑ i = 1 n d = n − 2 \sum_{i=1}^{n} d=n-2 i=1nd=n2,我们将和为 n − 2 − i n-2-i n2i的最小集合去掉后就得到和为 i i i的最大集合。
可得答案
∑ i = 0 n − 2 ∣ M ( i ) ∣ − ∣ m ( n − 2 − i ) ∣ \sum_{i=0}^{n-2}|M(i)|-|m(n-2-i)| i=0n2M(i)m(n2i)

时间复杂度 O ( n n ) O\left(n\sqrt{n}\right) O(nn )

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int n1=200;
const int lim=100000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,a[MAXN],b[MAXN],dp[MAXN],gp[MAXN],tmp[MAXN];
int q[MAXN],head,tail,sum[MAXN],deg[MAXN];LL ans;
signed main(){
	read(n);
	for(int i=1;i<n;i++)
		read(a[i]),read(b[i]),deg[a[i]]++,deg[b[i]]++;
	for(int i=1;i<=n;i++)deg[i]--,sum[deg[i]]++,dp[i]=INF;dp[0]=0;
	for(int i=1;i<=n;i++){
		if(!sum[i])continue;
		for(int j=0;j<i;j++){
			head=tail=1;
			for(int k=j;k<=n-2;k+=i){
				while(head<tail&&k-q[head]>sum[i]*i)head++;
				while(head<tail&&dp[q[tail-1]]+(k-q[tail-1])/i>=dp[k])tail--;
				q[tail++]=k;tmp[k]=dp[q[head]]+(k-q[head])/i;
			}
		}
		for(int j=1;j<=n-2;j++)dp[j]=tmp[j];
	}
	for(int i=0;i<=n-2;i++)gp[n-2-i]=n-dp[i];
	for(int i=0;i<=n-2;i++)if(dp[i]<INF-1)ans+=1ll*(gp[i]-dp[i]+1);
	printf("%lld\n",ans);
	return 0;
}

谢谢!!!

posted @ 2021-08-23 20:53  StaroForgin  阅读(9)  评论(0)    收藏  举报  来源