zoukankan      html  css  js  c++  java
  • 「HNOI 2015」实验比较

    (Description)

    (n)个元素,对于每个元素(x_i)最多知道一个形如(x_j < x_i)(x_j=x_i)的条件,问有多少合法的序列.合法的序列满足每个元素出现一次,任一相邻两元素之间有小于号或或等于号,并且所有条件全部满足,但是对于两个序列,如果只修改相等元素的位置能使得他们一样,则是他们为同一序列,答案队(10^9)取模

    (n<=100)

    (Solution)

    认真读题后可以发现:

    对于每个元素(x_i)最多知道一个形如(x_j<x_i)(x_j=x_i)的条件

    (x_j=x_i)

    ((i,j))合并,

    (x_j<x_i),

    (j)连向(i)

    最后建立一个超级根节点,将这个点连向所有入读为(0)的点

    这样就是一棵树了.

    (f[i][j])表示以(i)为根的子树中的元素能分(j)段,使(j)段每段相等的合法序列数

    首先给出转移方程:

    [f[x][i]=f'[x][j]*f[v][k]*C_{i-1}^{j-1}*C_{j-1}^{k-i+j} ]

    (f'[x])表示在(v)之前的(f)

    v为x的子树

    [C_{i-1}^{j-1}*C_{j-1}^{k-i+j} ]

    上面式子表示将(j)段和(k)段合并为(i)段的方案数.

    至于为什么?

    就是(j-1)放在(x)的方案数(*v)剩下的段余B合并的方案数:

    (Code)

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    int read(){
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
        return x*f;
    }
    int c[1011][1011],f[1001][1001],siz[10011],dp[10010],head[10010],cnt;
    struct node {
        int to,next;
    }a[10011];
    void add(int x,int y){
        a[++cnt].to=y,a[cnt].next=head[x],head[x]=cnt;
        a[++cnt].to=x,a[cnt].next=head[y],head[y]=cnt;
    }
    void init(){
        for(int i=0;i<=200;i++)
        c[i][i]=c[i][0]=1;
        for(int i=2;i<=200;i++)
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    }
    void dfs(int x,int fa){
        siz[x]=f[x][1]=1;
        for(int e=head[x];e;e=a[e].next){
            int v=a[e].to;
            if(v==fa)
            continue;
            dfs(v,x);
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=siz[x]+siz[v];i++)
                for(int j=1;j<=siz[x];j++)
                    for(int k=1;k<=siz[v];k++){
                        int z=k-i+j;
                        if(z<0) continue;
                        (dp[i]+=f[x][j]*f[v][k]%mod*c[i-1][j-1]%mod*c[j-1][z]%mod)%=mod;
                    }
            siz[x]+=siz[v];
            for(int i=1;i<=siz[x];i++)
                f[x][i]=dp[i];
        }
    }
    int X[100001],Y[10001],pre[10001],flag[10010],bj[10010],vis[10010],fa[10010];
    int find(int x){
        return (x==pre[x])?x:pre[x]=find(pre[x]);
    }
    int find1(int x){
        return (x==fa[x])?x:fa[x]=find1(fa[x]);
    }
    void join(int x,int y){
        int fx=find(x),fy=find(y);
        if(fx!=fy)
        pre[fx]=fy,fa[fx]=fy;
    }
    main(){
        init();
        int n=read(),m=read(),x,y;
        char s;
        for(int i=1;i<=n;i++)
            pre[i]=i,fa[i]=i;
        for(int i=1;i<=m;i++){
            X[i]=read(),scanf("%c",&s),Y[i]=read();
            if(s=='=')
                flag[i]=1,join(X[i],Y[i]);
        }
        for(int i=1;i<=m;i++)
            if(!flag[i]){
                int fx=find(X[i]),fy=find(Y[i]);
                add(fx,fy),vis[fy]++;
                if(find1(X[i])==find1(Y[i]))
                    puts("0"),exit(0);
                fa[find1(X[i])]=find1(Y[i]);
        }
        for(int i=1;i<=n;i++)
            bj[find(i)]=1;
        for(int i=1;i<=n;i++)
            if(bj[i]&&!vis[i])
                add(n+1,i);
        dfs(n+1,0);
        int ans=0;
        for(int i=1;i<=siz[n+1];i++)
            ans+=f[n+1][i],ans%=mod;
        printf("%lld ",ans);
    }
    
  • 相关阅读:
    2015上阅读计划
    《梦断代码》读书笔记 第2篇
    四则运算3
    求数组中最大子数组的和(一维)
    四则运算2—单元测试
    四则运算2
    《梦断代码》读书笔记 第1篇
    四组运算2(思路)
    四则运算1
    读书笔记
  • 原文地址:https://www.cnblogs.com/hbxblog/p/10429443.html
Copyright © 2011-2022 走看看