您的位置:首页 > 其它

bzoj4013: [HNOI2015]实验比较

2015-12-22 14:20 309 查看
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4013

思路:首先把等于的缩成一个点,由好的向坏的连边,有环肯定无解。

然后题目里说“小 D都最多只记住了某一张质量不比 i
差的另一张图片 Ki”

那就是每个点就最多只有一条入边,那存在合法方案的图就一定是森林。

加一个虚根,这可以树形DP了。

假设f[i][j]表示i号点的子树中的所有点构成的有且只有j个小于号的序列的个数

那么考虑怎么合并两个子树的答案

假设现在的两个儿子是x,y,子树大小分别为siz[x],siz[y]

枚举两个儿子的序列小于号个数i,j。

那么合并出来的新序列小于号个数k就在max(i,j)到i+j之间。

那么问题就是求对于k个盒子,有i个白球,j个黑球,求有多少种方案。

答案就是f[x][i]*f[y][j]*C[k][i]*C[i][j-(k-i)] C是组合数。

实现的时候,再开一个g数组,g[i]就是之前的儿子的子树的点构成的小于号为j个的序列方案数

注意儿子合并完后,因为新加了当前点,序列长度要+1

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=105,maxm=205,mod=(int)(1e9+7);
typedef long long ll;
using namespace std;
int n,m,pre[maxm],now[maxn],son[maxm],tot,fa[maxn],cnt,deg[maxn],siz[maxn];char op[3];
ll c[maxn][maxn],f[maxn][maxn],ans,g[maxn];
bool bo[maxn];
struct node{int x,y;}li[maxn];
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,deg[b]++;}
int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}

bool dfs(int x,int fa){
bo[x]=1;bool fir=1;
for (int y=now[x];y;y=pre[y]) if (son[y]!=fa){
if (bo[son[y]]) return 0;
if (!dfs(son[y],x)) return 0;
if (!fir){
memset(g,0,sizeof(g));
for (int i=1;i<=siz[x];i++) if (f[x][i])
for (int j=1;j<=siz[son[y]];j++) if (f[son[y]][j])
for (int k=max(i,j);k<=i+j;k++)
g[k]=(g[k]+f[x][i]*f[son[y]][j]%mod*c[k][i]%mod*c[i][j-(k-i)]%mod)%mod;
siz[x]+=siz[son[y]];
for (int i=1;i<=siz[x];i++) f[x][i]=g[i];
}
else{
fir=0,siz[x]=siz[son[y]];
for (int i=1;i<=siz[son[y]];i++) f[x][i]=f[son[y]][i];
}
}
if (x){
siz[x]++;if (fir) f[x][1]=1;
else for (int i=siz[x];i>=1;i--) f[x][i]=f[x][i-1];
}
return 1;
}

int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=0;i<=n;i++) c[i][0]=1;
for (int i=1;i<=n;i++) for (int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
for (int i=1,x,y;i<=m;i++){
scanf("%d%s%d",&x,op,&y);
if (op[0]=='='){fa[getfa(x)]=getfa(y);continue;}
if (op[0]=='>') swap(x,y);
li[++cnt]=(node){x,y};
}
for (int i=1;i<=cnt;i++){
int x=li[i].x,y=li[i].y;
if (getfa(x)!=getfa(y)) add(getfa(x),getfa(y));
else return puts("0"),0;
}
for (int i=1;i<=n;i++) if (!deg[getfa(i)]) add(0,getfa(i));
if (!dfs(0,-1)) return puts("0"),0;
//for (int i=1;i<=n;i++) printf("%d %lld\n",i,f[0][i]);
for (int i=1;i<=siz[0];i++) ans=(ans+f[0][i])%mod;
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: