1. 程式人生 > 其它 >[atARC125F]Tree Degree Subset Sum

[atARC125F]Tree Degree Subset Sum

令$a_{i}$為$i$的度數-1,那麼$(x,s)$合法即等價於存在$S\subseteq [1,n],|S|=x$且$\sum_{k\in S}a_{k}=s$

引理:$(x,s)$合法的必要條件為$-z\le s-x\le z-2$

令$z$為$a_{i}$中為0的元素個數,考慮任意一個集合$S\subseteq [1,n]$,顯然$-z\le \sum_{k\in S}a_{k}-|S|\le z-2$

具體的,考慮該式即為$\sum_{k\in S}(a_{k}-1)$,那麼將所有$a_{k}<1$的項求和即為最小值,將所有$a_{k}\ge 1$的項求和即為最大值,不難發現前者即為$-z$,後者即為$\sum_{i=1}^{n}a_{i}-(n-z)=z-2$

不難發現,其所描述的即為該引理,也即得證

結論:令$mn(s)=\min_{(x,s)合法}x$和$mx(s)=\max_{(x,s)合法}x$,則$\forall mn(s)\le x\le mx(s),(x,s)$合法

(為了方便,若不存在$(x,s)$合法則定義$mn(s)$和$mx(S)$為$\pm\infty$,顯然此時滿足結論)

同樣令$z$為$a_{i}$中為0的元素個數,顯然$mn(s)$對應的方案必然一個0都不選,那麼在其基礎上再選$[0,z]$個0,即有$\forall mn(s)\le x\le mn(s)+z,(x,s)$合法

類似地,也可以得到$\forall mx(s)-z\le x\le mx(s),(x,s)$合法

由引理即有$mx(s)-mn(s)\le 2z-2$,因此兩者區間相交,也即得證

由此,問題即$\forall s$求$mn(s)$和$mx(s)$,這個問題可以dp解決

具體的,(以$mn(s)$為例)令$f_{s}$為對應的答案,則$f_{s}=\min(f_{s},f_{s-a_{i}}+1)$

進一步的,將其對每一種物品(指相同的$a_{i}$)一起處理,假設有$x$個$a_{i}=k$,列舉$s$上選擇的$k$個數,即可得到轉移為$f_{s}=\min_{0\le i\le x}(f_{s-ik}+i)$,不難用優先佇列$o(1)$求出後者

注意到物品種類數至多為$\sqrt{n}$,因此總複雜度為$o(n\sqrt{n})$,可以通過

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define ll long long
 5 int n,x,y,l,r,a[N],tot[N],q[N],mx[N],mn[N],f[N];
 6 ll ans;
 7 int main(){
 8     scanf("%d",&n);
 9     memset(a,-1,sizeof(a));
10     for(int i=1;i<n;i++){
11         scanf("%d%d",&x,&y);
12         a[x]++,a[y]++;
13     }
14     for(int i=1;i<=n;i++)tot[a[i]]++;
15     memset(mn,0x3f,sizeof(mn));
16     memset(mx,-0x3f,sizeof(mx));
17     mn[0]=0,mx[0]=tot[0];
18     for(int i=1;i<=n;i++){
19         if (!tot[i])continue;
20         for(int j=0;j<i;j++){
21             l=1,r=0;
22             for(int k=j;k<=n;k+=i){
23                 while ((l<=r)&&(mn[k]-k/i<=mn[q[r]]-q[r]/i))r--;
24                 q[++r]=k;
25                 while ((l<=r)&&(q[l]/i<k/i-tot[i]))l++;
26                 f[k]=mn[q[l]]+(k-q[l])/i;
27             }
28         }
29         memcpy(mn,f,sizeof(f));
30     }
31     for(int i=1;i<=n;i++){
32         if (!tot[i])continue;
33         for(int j=0;j<i;j++){
34             l=1,r=0;
35             for(int k=j;k<=n;k+=i){
36                 while ((l<=r)&&(mx[k]-k/i>=mx[q[r]]-q[r]/i))r--;
37                 q[++r]=k;
38                 while ((l<=r)&&(q[l]/i<k/i-tot[i]))l++;
39                 f[k]=mx[q[l]]+(k-q[l])/i;
40             }
41         }
42         memcpy(mx,f,sizeof(f));
43     }
44     for(int i=0;i<=n;i++)ans+=max(mx[i]-mn[i]+1,0);
45     printf("%lld\n",ans);
46     return 0;
47 }
View Code