HNOI2015实验比较--树型DP

我看了一下,网上好像没有跟我一样做法的,而且我根本学不会那个做法。那我只好发个博客啦(做法也许有错)!

题意

n n 个物品和m个条件,每个条件表示 i i 的权值小于或等于j的权值,每个 j j 只会出现一次。
要求给每个物品分配一个权值,使得满足所有的条件,并且1 MAX M A X 的每个值都出现了(其中 MAX M A X 指最大权值)。

分析

首先 i=j i = j 的条件很好搞,直接用个并查集合并就行了。
然后每个 j j 只会出现一次,所以就可以把i当做其父亲。
如果出现了环,就一定没有满足条件的方案。
那么原问题可以转化为:一棵森林,给每个点分配一个权值,使得
1、每个点的权值比父亲大;
2、 1 1 MAX的每个值都出现了。
为了方便,可以算每个点权值比父亲小,意义是一样的。
dp[i][j] d p [ i ] [ j ] 为考虑了 i i 这棵子树,i的权值不大于 j j 时的方案数,那么不考虑2条件,那么

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

dp[0][j] d p [ 0 ] [ j ] 为考虑所有点,最大权值为 j j 时的方案数,则
dp[0][i]=u is a rootdp[u][j]

接下来考虑2条件。设 f[i] f [ i ] 为最大值为 i i 并且每个值都出现了的方案数。可以得到
f[i]=dp[0][i]j=1i1f[j]Cij

那么 f[i] ∑ f [ i ] 就是答案了。
时间复杂度: O(n2) O ( n 2 )

代码

#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;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值