zoukankan      html  css  js  c++  java
  • BZOJ[4013] [HNOI2015]实验比较

    树DP+组合数

    网上题解很多,这里就放个有注释的代码

    CODE

      1 #include <cmath>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <cstdlib>
      5 #include <iostream>
      6 #include <algorithm>
      7 # define maxn 110
      8 # define mod 1000000007
      9 using namespace std;
     10 typedef long long LL;
     11 LL C[maxn][maxn];
     12 void GetC(){
     13     C[0][0]=1;
     14     for(int i=1;i<=100;i++){
     15         C[i][0]=1;
     16         for(int j=1;j<=i;j++){
     17             C[i][j]=C[i-1][j-1]+C[i-1][j];
     18             C[i][j]%=mod;
     19         }
     20     }
     21 }
     22 int n,m,N;
     23 struct node{
     24     int u,v,nxt;
     25 }g[10010];
     26 int adj[maxn],e,ru[maxn];
     27 void add(int u,int v){
     28     g[e]=(node){u,v,adj[u]};
     29     adj[u]=e++;
     30 }
     31 int fa[maxn];
     32 int find_fa(int x){
     33     if(x==fa[x]) return x;
     34     return fa[x]=find_fa(fa[x]);
     35 }
     36 bool vis[maxn];
     37 void init(){
     38     scanf("%d%d",&n,&m);
     39     for(int i=1;i<=n;i++) fa[i]=i;
     40     memset(adj,-1,sizeof(adj));
     41     int x,y; char s[3];
     42     for(int i=1;i<=m;i++){
     43         scanf("%d%s%d",&x,&s,&y);
     44         int a=find_fa(x),b=find_fa(y);
     45         if(s[0]=='='){
     46             fa[b]=a;
     47         }
     48         else{
     49             add(a,b); ru[b]++;
     50         }
     51     }
     52     for(int i=1;i<=n;i++){
     53         int a=find_fa(i);
     54         if(!vis[a]){
     55             vis[a]=1; N++;
     56             if(!ru[a]) add(0,a);
     57         }
     58     }
     59 }
     60 int q[maxn],head,tail;
     61 bool check(){
     62     int cnt=0;
     63     memset(vis,0,sizeof(vis));
     64     for(int i=1;i<=n;i++){
     65         int a=find_fa(i);
     66         if(!vis[a] && !ru[a]) q[tail++]=a,vis[a]=1;
     67     }
     68     while(head<tail){
     69         int k=q[head++]; cnt++;
     70         for(int i=adj[k];i!=-1;i=g[i].nxt){
     71             int v=g[i].v;  ru[v]--;
     72             if(!ru[v]) q[tail++]=v;
     73         }
     74     }
     75     if(cnt!=N){cout<<"0"<<endl; return 0;}
     76     return 1;
     77 }
     78 LL f[maxn][maxn]; 
     79 // f[i][j] 表示以i节点为根的子树合成的序列中有j个小于号的方案数
     80 //能出现小于号数的变化是因为两个字数中的序列可以合并
     81 //例如: 1<2 ; 3<4; 可以合并成 1=3<2=4 
     82 LL p[maxn];
     83 int size[maxn];
     84 void dfs(int x){
     85     if(adj[x]==-1){f[x][1]=1; size[x]=1; return;}
     86     size[x]=0;
     87     bool ok=0;
     88     for(int i=adj[x];i!=-1;i=g[i].nxt){
     89         int v=g[i].v; dfs(v);
     90         if(!ok){ //遇到第一个儿子可以直接复制过来, 
     91             ok=1;
     92             for(int j=0;j<=size[v];j++){
     93                 f[x][j]+=f[v][j];
     94             }
     95             size[x]+=size[v];
     96             continue;
     97         }
     98         memset(p,0,sizeof(p));
     99         for(int j=1;j<=size[x];j++){ //枚举i节点子树中已合并的 
    100             for(int k=1;k<=size[v];k++){  //枚举儿子 
    101                 if(!f[x][j] || !f[v][k]) continue;
    102                 for(int h=max(j,k);h<=j+k;h++){
    103                     p[h]=(p[h]+f[x][j]*f[v][k]%mod*C[h][j]%mod*C[j][k-(h-j)]%mod)%mod;
    104                     // 把j+k个合并成h个,相当于把1,0放入h个盒子,相同的不能在一个盒子
    105                     //不能有空盒子的方案数
    106                     //那么可以选j个盒子,把1全部放进去
    107                     //然后剩下的空盒子用k补全
    108                     //最后用剩下的0 放到有1的盒子里 
    109                 }
    110             }
    111         }
    112         memcpy(f[x],p,sizeof(f[x]));
    113         size[x]+=size[v];
    114     }
    115     size[x]++;
    116     if(x){//不是虚根 
    117         for(int i=size[x];i>=1;i--) f[x][i]=f[x][i-1]; 
    118         //父亲上的小于号是没有办法合并的,所以之前的所有方案数的j都要+1 
    119     }
    120 }
    121 void work(){
    122     LL ans=0;
    123     dfs(0);
    124     for(int i=1;i<=n;i++) ans+=f[0][i],ans%=mod;
    125     cout<<ans<<endl;
    126 }
    127 int main(){
    128 //    freopen("a.in","r",stdin);
    129     init();
    130     GetC();
    131     if(check()) work();
    132 }
    View Code
  • 相关阅读:
    问题 F: A+B和C (15)
    问题 E: Shortest Distance (20)
    完数
    分解质因数
    念整数
    问题 B: 习题7-7 复制字符串中的元音字母
    问题 A: 习题7-5 字符串逆序存放
    问题 D: 习题6-12 解密
    计算机的重点编码方式
    PyCharm更换第三方包源
  • 原文地址:https://www.cnblogs.com/FOXYY/p/7676189.html
Copyright © 2011-2022 走看看