bzoj1494 生成树计数 (dp+矩阵快速幂)

Posted ressed

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1494 生成树计数 (dp+矩阵快速幂)相关的知识,希望对你有一定的参考价值。

题面欺诈系列...

因为一个点最多只能连到前k个点,所以只有当前的连续k个点的连通情况是对接下来的求解有用的

那么就可以计算k个点的所有连通情况,dfs以下发现k=5的时候有52种。

我们把它们用类似于并查集的方式表达(比如12132代表点1和点3连通,2和5连通,3自己),然后再压缩一下。

但要注意的是,12132和23213这两种实际对应的是一种连通情况,我们只要把它都化成字典序最小的那种就可以了

然后考虑增加一个新点以后状态的转移,可以枚举这个点与前k个点(始状态S)的连边情况,其中有一些是不合法的:

  1.连到了两个本来就连通的点上(导致成环)

  2.在1号点不与其他点连通的情况下,没有连到1号点(导致不连通)

然后再根据连边情况得到终状态E,将trans[S][E]++。最后trans[i][j]表示的就是加入一个点后由i状态到j状态的方案数

那么就可以得到递推式$f[i][j]=sum^{总状态数}_{k=1}{f[i-1][k]*trans[k][j]}$,其中f[i][j]表示以i为结尾的k个点状态是j的方案数

那么答案就是f[N][1](假设1是都连通的状态)

然后初值就应该是f[K][i]=i状态的方案数,其中i状态的方案数为它其中每个联通块方案数的乘积。

那么联通块的方案数怎么算呢?其实题面已经说了...n个点的方案数就是$n^{n-2)}$

然后就可以愉快地dp啦...

诶?$N<=10^{15}$?

大概能卡过去吧(大雾)

容易发现递推的形式其实和矩阵乘法是相同的,把f[i]和trans看作矩阵,就是$f[N]=f[K]*trans^{N-K}$

然后就可以倍增做矩阵快速幂了。方法和整数的快速幂是一样的。

复杂度:

  K=5的状态数接近50,$log(10^{15})$接近50。

  所以基本上是$50^3*50=6250000$的。

代码写的很蛋疼...

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<map>
  5 #include<vector>
  6 #define LL long long 
  7 #define inf 0x3f3f3f3f
  8 using namespace std;
  9 const LL maxn=1e15+5,maxs=55,p65=50000,mod=65521;
 10 
 11 LL rd(){
 12     LL x=0;char c=getchar();int neg=1;
 13     while(c<0||c>9){if(c==-) neg=-1;c=getchar();}
 14     while(c>=0&&c<=9) x=x*10+c-0,c=getchar();
 15     return x*neg;
 16 }
 17 
 18 LL N;int K,sct,num[6]={0,1,1,3,16,125};
 19 LL f[maxs];
 20 LL trans[2][maxs][maxs],out[2][maxs][maxs];
 21 struct ST{
 22     int s[6];
 23     ST(int x=0){memset(s,x,sizeof(s));}
 24 }sta[maxs];
 25 struct Mat{
 26     LL m[maxs][maxs];
 27     Mat(){memset(m,0,sizeof(m));}
 28 };
 29 int mp[p65];
 30 
 31 void print(ST s){
 32     for(int i=1;i<=K;i++) printf("%d",s.s[i]);
 33 }
 34 int STtoInt(ST s){
 35     int x=0;for(int i=1;i<=K;i++) x=x*6+s.s[i];return x;
 36 }
 37 
 38 ST toLeast(ST s){
 39     ST flag=ST(-1),re=ST();int n=0;
 40     for(int i=1;i<=K;i++){
 41         if(flag.s[s.s[i]]==-1) flag.s[s.s[i]]=n++;
 42         re.s[i]=flag.s[s.s[i]];
 43     }return re;
 44 }
 45 
 46 void dfs1(int x,ST s,int m){
 47     if(x>=K+1){
 48         sta[++sct]=s;mp[STtoInt(s)]=sct;return;
 49     }
 50     for(int i=0;i<=m+1;i++){
 51         s.s[x]=i;
 52         dfs1(x+1,s,max(m,i));
 53     }
 54 }
 55 
 56 void dfs2(int x,ST s,ST from){
 57     if(x>=K+1){
 58         bool flag[6],bb=s.s[1];ST to=ST();int mi=5;
 59         memset(flag,0,sizeof(flag));
 60         //print(from);printf("	");print(s);printf("
");
 61         for(int i=1;i<=K;i++){
 62             if(!s.s[i]) continue;
 63             if(flag[from.s[i]]) return;
 64             flag[from.s[i]]=1;mi=min(mi,from.s[i]);
 65         }
 66         for(int i=2;i<=K;i++){
 67             to.s[i-1]=flag[from.s[i]]?mi:from.s[i];
 68             if(from.s[1]==to.s[i-1]) bb=1;
 69         }to.s[K]=mi;
 70         if(!bb) return;
 71         to=toLeast(to);
 72         //print(from);printf("	");print(s);printf("	");print(to);printf("
");
 73         trans[0][mp[STtoInt(from)]][mp[STtoInt(to)]]++;
 74         
 75     }else{
 76         dfs2(x+1,s,from);s.s[x]=1;dfs2(x+1,s,from);
 77     }
 78 }
 79 
 80 void sets(){
 81     for(int i=1;i<=sct;i++){
 82         dfs2(1,ST(),sta[i]);
 83         //for(int j=1;j<=sct;j++) printf("%d ",trans[0][i][j]);printf("
");
 84         ST cnt=ST();
 85         for(int j=1;j<=K;j++) cnt.s[sta[i].s[j]]++;
 86         f[i]=1;for(int j=0;j<K;j++) if(num[cnt.s[j]])f[i]*=num[cnt.s[j]];
 87     }
 88 }
 89 
 90 void solve(){
 91     LL p=N-K;bool b=1,c=1;int i,j,k;
 92     for(i=1;i<=sct;i++) out[0][i][i]=1;
 93     while(p){
 94         if(p&1){
 95             memset(out[c],0,sizeof(out[c]));
 96             for(i=1;i<=sct;i++){
 97                 for(j=1;j<=sct;j++){
 98                     for(k=1;k<=sct;k++){
 99                         out[c][i][j]=(out[c][i][j]+out[c^1][i][k]*trans[b^1][k][j])%mod;
100                     }
101                 }
102             }c^=1;
103         }
104         memset(trans[b],0,sizeof(trans[b]));
105         for(i=1;i<=sct;i++){
106             for(j=1;j<=sct;j++){
107                 for(k=1;k<=sct;k++){
108                     trans[b][i][j]=(trans[b][i][j]+trans[b^1][i][k]*trans[b^1][k][j])%mod;
109                 }
110             }
111         }p>>=1;b^=1;
112     }LL ans=0;
113     for(i=1;i<=sct;i++){
114         ans=(ans+f[i]*out[c^1][i][1])%mod;
115     }printf("%d
",ans);
116 }
117 
118 int main(){
119     int i,j,k;
120     K=rd(),N=rd();
121     dfs1(2,ST(),0);sets();
122     solve();
123     return 0;
124 }

 

以上是关于bzoj1494 生成树计数 (dp+矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章

[HNOI2008][bzoj1009] GT考试 [KMP+矩阵快速幂]

指数型生成函数的范例——状压dp矩阵快速幂优化及对指数型生成函数的理解

[BZOJ2553][BeiJing2011]禁忌 dp+AC自动机+矩阵快速幂

[bzoj1875][SDOI2009] HH去散步 [dp+矩阵快速幂]

[BZOJ4887][TJOI2017]可乐(DP+矩阵快速幂)

BZOJ 2323 2323: [ZJOI2011]细胞 (DP+矩阵乘法+快速幂*)