• [noi31]MST


    定义dp[i]表示当前连通块状态为i的方案数(状态记录该状态每一个连通块的大小),那么从小到大枚举每条边,考虑这条边在不在最小生成树上:

    1. 如果不在最小生成树上,那么这条边有$\sum_{i=1}^{scc}\sum_{j=i+1}^{scc}Si\cdot Sj$Si表示第i个连通块的点数)种位置,即每一个状态的方案都乘上这个数;

    2. 如果在最小生成树上,那么他一定会把某两个连通块连起来,枚举这两个连通块并递推到新的状态。

    那么这样的时间复杂度是多少呢?大约是$o(Pn\cdot n^{3})$Pn表示将n划分成一些数的和的方案数,$P_{40}\approx 40000$,$n^{2}$是枚举连通块,另一个nhash的时间),显然这样是会炸掉的……

    似乎这个状态的记录方式可以改变,可改为每一个大小的连通块出现次数,由于最多只有$\sqrt{n}$种方案,枚举两个连通块也仅有n的时间复杂度,总时间复杂度降为$o(Pn\cdot n^{2})$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 map<int,int>vis;
     5 int n,m,k,di[40001][41],a[41],mi[41],b[1001],f[40001];
     6 vector<int>vec[40001];
     7 void dfs(int k,int s,int p){
     8     if (!s){
     9         memcpy(di[++m],a,sizeof(a));
    10         di[m][0]=k;
    11     }
    12     if (s<1)return;
    13     k++;
    14     for(int i=1;i<=p;i++){
    15         a[i]++;
    16         dfs(k,s-i,i);
    17         a[i]--;
    18     }
    19 }
    20 int ha(int k){
    21     int ans=0;
    22     for(int i=1;i<=n;i++)ans=(ans+1LL*mi[i]*di[k][i])%mod;
    23     return ans;
    24 }
    25 int main(){
    26     mi[0]=1;
    27     for(int i=1;i<=40;i++)mi[i]=mi[i-1]*41LL%mod; 
    28     scanf("%d",&n);
    29     dfs(0,n,n);
    30     for(int i=1;i<=m;i++)vec[di[i][0]].push_back(i);
    31     for(int i=1;i<n;i++){
    32         scanf("%d",&k);
    33         b[k]=1;
    34     }
    35     f[1]=1;
    36     m=n*(n-1)/2;
    37     k=n;
    38     for(int i=1;i<=m;i++)
    39         if (!b[i])
    40             for(int j=0;j<vec[k].size();j++){
    41                 int v=vec[k][j],s=m-i+1;
    42                 for(int x=1;x<=n;x++)
    43                     for(int y=x+1;y<=n;y++)s-=di[v][x]*di[v][y]*x*y;
    44                 for(int x=1;x<=n;x++)s-=(di[v][x]-1)*di[v][x]*x*x/2;
    45                 f[vec[k][j]]=f[vec[k][j]]*1LL*s%mod; 
    46             }
    47         else{
    48             for(int j=0;j<vec[k-1].size();j++)vis[ha(vec[k-1][j])]=vec[k-1][j];
    49             for(int j=0;j<vec[k].size();j++){
    50                 int v=vec[k][j];
    51                 for(int x=1;x<=n;x++)
    52                     for(int y=x+1;y<=n-x;y++){
    53                         if ((!di[v][x])||(!di[v][y]))continue;
    54                         di[v][x]--;
    55                         di[v][y]--;
    56                         di[v][x+y]++;
    57                         f[vis[ha(v)]]=(f[vis[ha(v)]]+f[v]*(di[v][x]+1LL)*(di[v][y]+1)*x*y)%mod;
    58                         di[v][x]++;
    59                         di[v][y]++;
    60                         di[v][x+y]--;
    61                     }
    62                 for(int x=1;x<=n/2;x++)
    63                     if (di[v][x]>1){
    64                         di[v][x]-=2;
    65                         di[v][x+x]++;
    66                         f[vis[ha(v)]]=(f[vis[ha(v)]]+f[v]*(di[v][x]+2LL)*(di[v][x]+1)*x*x/2)%mod;
    67                         di[v][x]+=2;
    68                         di[v][x+x]--;
    69                     }
    70             }
    71             k--;
    72             for(int j=0;j<vec[k].size();j++)vis[ha(vec[k][j])]=0;
    73         }
    74     printf("%d",f[vec[1][0]]);
    75 }
    View Code
  • 相关阅读:
    用OSCache进行缓存对象
    oscache的使用
    WHM(Web Hosting Manager)/CPANEL 设置及linux 文件权限 经验
    辗转相除法求最大公约数和最小公倍数【gcd】
    u6填写入库单单据报错
    账表名称无法显示
    商贸通反利怎么计算
    UFO报表转换不成功!请检查文件版本或使用DOS文件转换工具
    备份计划不执行
    821升级U6报错
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/11272189.html
Copyright © 2020-2023  润新知