[NOIP2019模拟赛]HC1147 时空阵

题目描述:

幽香这几天学习了魔法,准备建造一个大型的时空传送阵。

幽香现在可以在幻想乡的n个地点建造一些传送门,如果她建造了从地点a与地点b之间的传送门,那么从a到b和从b到a都只需要单位1的时间。

同时这些地点之间在地理上是非常遥远的,因此来往他们必须使用传送门。

现在幽香想要问你,有多少种建造传送门的方案,使得地点1和地点n之间的最短距离恰好为k?两个方案不同当且仅当建造的传送门的集合不同。不能建造节点到自身的传送门,两个点之间也最多造一个传送门。

分析:

  DP...

  然而考场上没时间了,只打了20pts(k==1)

  为了防止变量名冲突,题面中的k在代码中都用m代替!!!

  首先:显然这道题的合法路径是一层一层的节点组成的

  然后就可以DP了(话说考场上状态设计都设计出来了,转移没时间了...)

  设f[i][j][k]表示当前进行到第i层,总共使用了j个节点,第i层使用了k个节点

  显然这个可以有f[i-1][j-k][s]转移过来,我们只要枚举s就可以了,现在我们只要计算f[i-1][j-k][s]的系数就好了

  分类讨论:

    当i<m时:

      ①可以当前层内部连边,也就是$2^{C_k^2}$种可能

      ②当前层与上一层连边,且当前层的所有点至少有一条边连出去,也就是$(\sum\limits_{p=1}^{s}C_s^p-1)^k$

        tip:上式子用二项式定理可以化简为$(2^{s}-1)^k$

      ③注意到当前这k个点是从剩下的n-j+k-1(最后减一是因为第n个点必须放在第m层)所以从这里面选k个点,方案数为$C_{n-j+k-1}^k$

    综上:$$f[i][j][s]=\sum f[i-1][j-k][s]*(2^s-1)^k*C_{n-j+k-1}^k*C_k^2$$

    当i==m时

      注意到只有第③个条件需要改变,也就是说第n个点肯定在这一层,所以只要在n-j+k-1中选择k-1的点即可,方案数为$C_{n-j+k-1}^{k-1}$

    即:$$f[i][j][s]=\sum f[i-1][j-k][s]*(2^s-1)^k*C_{n-j+k-1}^{k-1}*C_k^2$$

  预处理一下组合数,及时取模,结合快速幂就好了

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read(){
 5     int ans=0,f=1;char chr=getchar();
 6     while(!isdigit(chr)){if(chr=='-')f=-1;chr=getchar();}
 7     while(isdigit(chr)) {ans=(ans<<3)+(ans<<1)+chr-48;chr=getchar();}
 8     return ans*f;
 9 }const int P = 1e9+7;
10 int n,m,f[105][105][105],ans,cc[105][105];
11 int ksm(int x,int p){
12     int ans=1;
13     for(;p;p>>=1,x=x*x%P) if(p&1) ans=ans*x%P;
14     return ans;
15 }
16 int C(int x,int y){
17     int mul=1;
18     for(int i=2;i<=y;i++) mul=mul*i%P;
19     for(int i=2;i<=x-y;i++) mul=mul*i%P;
20     int t=ksm(mul,P-2);
21     mul=1;
22     for(int i=2;i<=x;i++) mul=mul*i%P;
23     mul=mul*t%P;
24 }
25 inline void Add(int &x,int y){x=x+y;if(x>=P)x-=P;}
26 inline void Solve(){
27     f[0][1][1]=1;
28     for(int i=1;i<=m;++i)
29         for(int j=1;j<=n;++j)
30             for(int k=1;k<=j;k++){
31                 for(int s=1;s<=j-k;s++)
32                     if(i<m)//第一种 
33                         Add(f[i][j][k],f[i-1][j-k][s]*ksm(2,(k-1)*k/2)%P*cc[n-j+k-1][k]%P*ksm(ksm(2,s)-1,k)%P);
34                     else//第二种 
35                         Add(f[i][j][k],f[i-1][j-k][s]*ksm(2,(k-1)*k/2)%P*cc[n-j+k-1][k-1]%P*ksm(ksm(2,s)-1,k)%P);
36             }
37     for(int i=m+1;i<=n;i++)
38         for(int j=1;j<=n;j++)
39             Add(ans,f[m][i][j]*ksm(2,(n-i)*(n-i-1)/2+j*(n-i)%P)%P);
40     cout<<ans<<endl;
41 }
42 signed main(){
43     freopen("timegate.in","r",stdin);
44     freopen("timegate.out","w",stdout);
45     for(int i=0;i<=100;i++)for(int j=0;j<=i;j++)cc[i][j]=C(i,j);//预处理组合数 
46     n=read(),m=read();
47     Solve();
48     return 0;
49 }

 

posted @ 2019-08-12 20:54  zheng_liwen  阅读(280)  评论(0编辑  收藏  举报
/*去广告*/