越来越菜,我该怎么办。
感觉是比较深刻的组合套路,场上不会就等着似掉吧。
题目链接。
题意
对满足如下要求的 
个点的无标号无根树计数:
- 每一个点有红蓝黄三种颜色之一;
- 红色点度数至多为 ,蓝黄点度数至多为 ;
- 黄点不与黄点相邻。
,模数为输入的质数。
题解
首先无标号无根树看起来就不好算。
这个时候有一个技巧,就是我们对无标号有根树进行计数。
由于一棵树最多有两个重心(如果有两个,那么这两个重心一定相邻),所以我们令根为中心进行计数。
设  表示一个大小为
的子树,根节点颜色为红/黄/蓝(并且根节点向上有连边),方案数是多少。
先考虑红蓝点的方案数,其对子树根的颜色没有要求,所以一个大小为  的子树为 ,不妨设其为 。
设  表示选了  个子树,总大小为  的方案数,那么 。
不难发现,我们这样会记重,因为我们记的是无根树,子树之间应当是没有顺序的。
那我们改一下状态,设 
表示只选择大小 
的子树,一共选了  棵,总大小为
,方案数是多少。
然后按照子树大小 
从小到大的顺序加入,假设加入 
个大小为  的子树,那么 。
这样就有  和 。
的转移也类似,只是子树的根不能为黄色。
下面是计算答案。因为我们强制令重心为根,所以子树大小一定 。
那么先枚举子树颜色,然后用  转移一下。
不难发现,我们这样没有计算上,有两个点同时为重心的情况。
那么枚举一下这两个点是什么颜色,再加上即可(注意两个点的颜色顺序,以及颜色相同时算重的情况)。
时间复杂度是 。可能需要把数组滚动一下。
代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 
 | #include<bits/stdc++.h>#define inf 0x3f3f3f3f3f3f3f3fll
 #define debug(x) cerr<<#x<<"="<<x<<endl
 using namespace std;
 using ll=long long;
 using ld=long double;
 using pli=pair<ll,int>;
 using pi=pair<int,int>;
 template<typename A>
 using vc=vector<A>;
 template<typename A,const int N>
 using aya=array<A,N>;
 inline int read()
 {
 int s=0,w=1;char ch;
 while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
 while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
 return s*w;
 }
 inline ll lread()
 {
 ll s=0,w=1;char ch;
 while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
 while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
 return s*w;
 }
 ll g[5][3005];
 ll h[5][3005];
 ll f[3005][3];
 ll vg[6],vh[6];
 ll inv[6];
 int n,mod;
 ll ans;
 inline ll qow(ll a,ll b)
 {
 ll ans=1;
 while(b)
 {
 if(b&1) ans=ans*a%mod;
 a=a*a%mod;
 b>>=1;
 }
 return ans;
 }
 inline ll C(ll a,int b)
 {
 ll ans=1;
 for(int i=1;i<=b;i++) ans=ans*(a-i+1)%mod*inv[i]%mod;
 return ans;
 }
 inline ll get(int col,int lim)
 {
 ll ans=0;
 if(col==0) for(int i=0;i<=3;i++) (ans+=g[i][lim])%=mod;
 if(col==1) for(int i=0;i<=2;i++) (ans+=g[i][lim])%=mod;
 if(col==2) for(int i=0;i<=2;i++) (ans+=h[i][lim])%=mod;
 return ans;
 }
 int main()
 {
 n=read(),mod=read();
 
 f[0][0]=f[0][1]=f[0][2]=1;
 g[0][0]=1,h[0][0]=1;
 for(int i=1;i<=5;i++) inv[i]=qow(i,mod-2);
 
 int lim=(n-1)/2;
 for(int i=1;i<=lim;i++)
 {
 for(int j=0;j<=3;j++) (f[i][0]+=g[j][i-1])%=mod;
 for(int j=0;j<=2;j++) (f[i][1]+=g[j][i-1])%=mod;
 for(int j=0;j<=2;j++) (f[i][2]+=h[j][i-1])%=mod;
 
 ll ch=(f[i][0]+f[i][1])%mod,cg=(ch+f[i][2])%mod;
 for(int p=0;p<=4;p++) vg[p]=C(p+cg-1,p),vh[p]=C(p+ch-1,p);
 for(int j=4;j>=0;j--) for(int s=0;s<=n;s++) for(int p=1;p<=j;p++)
 {
 if(s<p*i) continue;
 (g[j][s]+=g[j-p][s-p*i]*vg[p])%=mod;
 (h[j][s]+=h[j-p][s-p*i]*vh[p])%=mod;
 }
 }
 
 
 for(int i=0;i<=4;i++) (ans+=g[i][n-1])%=mod;
 for(int i=0;i<=3;i++) (ans+=g[i][n-1])%=mod;
 for(int i=0;i<=3;i++) (ans+=h[i][n-1])%=mod;
 
 if(n%2==0)
 {
 for(int i=0;i<3;i++) for(int j=i;j<3;j++)
 {
 if(i==j&&j==2) break;
 ll v1=get(i,lim),v2=get(j,lim);
 if(i!=j) (ans+=v1*v2)%=mod;
 else (ans+=C(v1+2-1,2))%=mod;
 }
 }
 printf("%lld\n",ans);
 return 0;
 }
 
 |