[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=n−2。
很明显,此时不同的
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
∑i⩾2cnti⩽cnt0−2。
可得
−
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
−cnt0⩽s−∣M(s)∣⩽s−∣m(s)∣⩽∑i⩾2cnti=cnt0−2,相当于从
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
(s−∣m(s)∣)−(s−∣M(s)∣)=∣M(s)∣−∣m(s)∣⩽cnt0−2+cnt0=2cnt0−2
而集合
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,y−x),所以我们统计出的每一个
(
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(nnlogn),我们仍然可以优化复杂度。
考虑将对于
i
∈
[
0
,
n
−
2
]
i\in[0,n-2]
i∈[0,n−2]我们可以将它们按模
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)\}
{dpx∣x∈[i−dsumd,i]∧x≡i(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)∣=n−∣m(n−2−i)∣,由于
∑
i
=
1
n
d
=
n
−
2
\sum_{i=1}^{n} d=n-2
∑i=1nd=n−2,我们将和为
n
−
2
−
i
n-2-i
n−2−i的最小集合去掉后就得到和为
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=0∑n−2∣M(i)∣−∣m(n−2−i)∣
时间复杂度 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;
}