解题:九省联考2018 秘密袭击CoaT

题面

按照*Miracle*的话来说,网上又多了一篇n^3暴力的题解

可能是因为很多猫题虽然很好,但是写正解性价比比较低?

 

直接做不可做,转化为统计贡献:$O(n)$枚举每个权值,直接统计第k大大于等于这个权值的联通块个数的和— —这样每个权值x恰会贡献x次。

将所有大于等于当前权值的点点权赋为1,其余点点权赋为零,然后就是$O(n^2)$树形背包:设$dp[i][j]$表示以i为根的子树里选出(新)点权和为j的联通块,且联通块必须包含i自身的方案数。

一些小小的卡常:unsigned int,减法取模,不够k个结束(这真的算卡常吗=。=)


 

正解需要生成函数知识,用整体DP的思想来做,线段树合并+拉格朗日插值

我们优化上面这个树形背包,考虑$f[i][j]$表示在以i为根的子树里选出点权和大于等于j的联通块数的生成函数,$g[i][j]$表示以i为根的子树所有点nde的$f[nde][j]$的和

f的转移是需要卷积的,g转移只需要加法,需要优化$f$的转移。先把f转成点值表达,这样就可以直接乘法了,最后再拉格朗日插值把多项式插出来。

转移是f的第二维对应位置相乘,然后用整体DP解决

上面四行都是我口胡的

 1 // luogu-judger-enable-o2
 2 #include<cstdio>
 3 #include<cctype>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define uint unsigned int
 7 using namespace std;
 8 const int N=2048;
 9 const uint mod=64123;
10 int n,k,w,t1,t2,cnt,tot;
11 int p[N],noww[2*N],goal[2*N];
12 int val[N],pro[N],siz[N],sze[N];
13 uint ans,dp[N][N];
14 void Read(int &x)
15 {
16     x=0; char ch=getchar();
17     while(!isdigit(ch))
18         ch=getchar();
19     while(isdigit(ch))
20         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
21 }
22 void Add(uint &x,uint y)
23 { 
24     x+=y;
25     if(x>=mod) x-=mod;
26 }
27 void Link(int f,int t)
28 {
29     noww[++cnt]=p[f];
30     goal[cnt]=t,p[f]=cnt;
31     noww[++cnt]=p[t];
32     goal[cnt]=f,p[t]=cnt;
33 }
34 void DFS(int nde,int fth)
35 {
36     register int i,j,h,g;
37     for(i=0;i<=sze[nde];i++) dp[nde][i]=0;
38     dp[nde][pro[nde]]=1,siz[nde]=pro[nde];
39     for(i=p[nde];i;i=noww[i])
40         if(goal[i]!=fth)
41         {
42             g=goal[i],DFS(g,nde);
43             for(j=siz[nde];~j;j--)
44                 for(h=siz[g];~h;h--)
45                     Add(dp[nde][j+h],dp[nde][j]*dp[g][h]%mod);
46             siz[nde]=siz[nde]+siz[g];
47         }
48     for(i=k;i<=siz[nde];i++) Add(ans,dp[nde][i]);
49 }
50 int main()
51 {
52     register int i,j;
53     Read(n),Read(k),Read(w);
54     for(i=1;i<=n;i++) Read(val[i]);
55     for(i=1;i<n;i++) Read(t1),Read(t2),Link(t1,t2);
56     for(i=1;i<=w;tot=0,i++)
57     {
58         for(j=1;j<=n;j++) pro[j]=val[j]>=i,tot+=pro[j];
59         if(tot<k) printf("%u",ans),exit(0); DFS(1,0),swap(siz,sze); 
60     }    
61     printf("%u",ans);
62     return 0;
63 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/10407663.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值