HNOI2015實驗比較--樹型DP

我看了一下,網上好像沒有跟我一樣做法的,而且我根本學不會那個做法。那我只好發個博客啦(做法也許有錯)!

題意

n 個物品和m 個條件,每個條件表示i 的權值小於或等於j 的權值,每個j 只會出現一次。
要求給每個物品分配一個權值,使得滿足所有的條件,並且1MAX 的每個值都出現了(其中MAX 指最大權值)。

分析

首先i=j 的條件很好搞,直接用個並查集合並就行了。
然後每個j 只會出現一次,所以就可以把i 當做其父親。
如果出現了環,就一定沒有滿足條件的方案。
那麼原問題可以轉化爲:一棵森林,給每個點分配一個權值,使得
1、每個點的權值比父親大;
2、1MAX 的每個值都出現了。
爲了方便,可以算每個點權值比父親小,意義是一樣的。
dp[i][j] 爲考慮了i 這棵子樹,i 的權值不大於j 時的方案數,那麼不考慮2條件,那麼

dp[i][j]=dp[i][j1]+u is a son of idp[u][j1]

dp[0][j] 爲考慮所有點,最大權值爲j 時的方案數,則
dp[0][i]=u is a rootdp[u][j]

接下來考慮2條件。設f[i] 爲最大值爲i 並且每個值都出現了的方案數。可以得到
f[i]=dp[0][i]j=1i1f[j]Cij

那麼f[i] 就是答案了。
時間複雜度:O(n2)

代碼

#include<bits/stdc++.h>
using namespace std;
#define REP(i,st,ed) for(int i=(int)(st),i##end=(int)(ed);i<=i##end;++i)
#define DREP(i,st,ed) for(int i=(int)(st),i##end=(int)(ed);i>=i##end;--i)
template<typename T>bool chkmin(T &x,const T &y){return x>y?x=y,1:0;}
template<typename T>bool chkmax(T &x,const T &y){return x<y?x=y,1:0;}
#ifdef __linux__
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif
template<typename T>T read(){
    T x=0,f=1;
    char c=getchar();
    while((c<'0')||(c>'9')){if(c=='-')f=-1;c=getchar();}
    while((c>='0')&&(c<='9'))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define read() read<int>()
template<typename T>void write(T x,char c){
    static char t[25];
    static int tlen;
    t[tlen=1]=c;
    if(x<0)putchar('-'),x=-x;
    do t[++tlen]=(x%10)^48;
    while(x/=10);
    while(tlen)putchar(t[tlen--]);
}
#define pb push_back
typedef long long ll;
typedef double lf;
const int maxn=105,mod=1e9+7;
int n,m,tot;
int fa[maxn],id[maxn];
int C[maxn][maxn];
void add(int &x,const int &y){
    x+=y;
    if(x<0)x+=mod;
    if(x>=mod)x-=mod;
}
void init(){
    int n=100;
    REP(i,0,n)
        C[i][0]=1;
    REP(i,1,n)
        REP(j,1,n)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
int find_fa(int x){
    return x==fa[x]?x:fa[x]=find_fa(fa[x]);
}
bool p[maxn][maxn],p2[maxn][maxn],indeg[maxn];
int lim,dp[maxn][maxn],f[maxn],vis_cnt;
vector<int>E[maxn];
void DFS(int u){
    vis_cnt+=(u!=0);
    REP(i,0,E[u].size()-1)DFS(E[u][i]);
    REP(i,1,tot){
        dp[u][i]=1;
        REP(j,0,E[u].size()-1){
            int v=E[u][j];
            dp[u][i]=1ll*dp[u][i]*dp[v][i-(u>0)]%mod;
        }
        if(u>0)
            add(dp[u][i],dp[u][i-1]);
    }
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
#endif
    init();
    n=read(),m=read();
    REP(i,1,n)fa[i]=i;
    int u,v;
    char s[10];
    REP(i,1,m){
        scanf("%d%s%d",&u,s,&v);
        if(s[0]=='=')fa[find_fa(v)]=find_fa(u);
        else p[u][v]=1;
    }
    REP(i,1,n)
        if(fa[i]==i)
            id[i]=++tot;
    REP(i,1,n)
        if(fa[i]!=i)
            id[i]=id[find_fa(i)];
    REP(i,1,n)
        REP(j,1,n)
            if(p[i][j]){
                p2[id[i]][id[j]]=1;
                indeg[id[j]]=1;
            }
    REP(i,1,tot)
        REP(j,1,tot)
            if(p2[i][j])
                E[i].pb(j);
    REP(i,1,tot)
        if(!indeg[i])
            E[0].pb(i);
    DFS(0);
    int ans=0;
    REP(i,1,tot){
        f[i]=dp[0][i];
        REP(j,1,i-1)
            add(f[i],-1ll*f[j]*C[i][j]%mod);
        add(ans,f[i]);
    }
    write(vis_cnt==tot?ans:0,'\n');
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章