zoukankan      html  css  js  c++  java
  • 【bzoj4013】 HNOI2015—实验比较

    http://www.lydsy.com/JudgeOnline/problem.php?id=4013 (题目链接)

    题意

      给出$n$个数的$m$个大小关系,问它们之间可以形成的单调不降的序列有多少种。

    Solution

      首先,因为等于号相连的两个数位置互换不会产生新的方案,我们先用并查集把用等号相连的点全部缩成一个。如果此时的图中出现了环,那么答案为$0$。考虑答案不为$0$的情况怎么处理。此时的图已经成为了一个DAG,我们需要在上面统计方案。容易发现,对于一个点,有分有合,合的情况很好处理,分的情况就很尴尬了,什么?你说每一个点只有一条入边?(哔了狗了)。因为并查集缩点后整个图已经变成了一棵树,我们考虑如何进行树形dp。

      $f[x][i]$表示在$x$的子树中,组成的序列用$<$相连的等价类个数为$i$个的序列方案,其中等价类就表示由等号相连的一坨数。不妨设$y$是$x$的某个儿子,那么转移:

    egin{aligned}  g[i+l]=f[x][i]*f[y][j]*inom{i-1+l}{j-1}*inom{j-1}{k-l}   end{aligned}

      其中$iin[1,size[x]]$,$jin[1,size[y]]$,$lin[max(0,k-j+1),k]$。$g$是一个临时的存储答案的数组。$l$是我们枚举的$y$所贡献的等价类,那么剩下的$k-l$就是$y$中与原本$x$中相等的数的个数。$x$永远排在序列首位而且不会与任意一个数相等。$inom{i-1+l}{j-1}$表示两个无相对关系的已经排序好的序列合并为一个序列的方案。$inom{j-1}{k-l}$表示在$x$的$j-1$个数中选出与$y$的$k-l$个数相等的数的方案。

    细节

      可能是个森林,所以用一个超级源点连向若干根节点。

    代码

    // bzoj4013
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #define LL long long
    #define inf (1ll<<30)
    #define MOD 1000000007
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=200;
    LL C[maxn][maxn],f[maxn][maxn],g[maxn];
    int head[maxn],size[maxn],vis[maxn],fa[maxn],r[maxn],n,m,c,cnt,sum;
    struct data {int u,v;}a[maxn];
    struct edge {int to,next;}e[maxn];
    
    int find(int x) {
    	return x==fa[x] ? x : fa[x]=find(fa[x]);
    }
    void link(int u,int v) {
    	e[++cnt]=(edge){v,head[u]};head[u]=cnt;
    }
    bool bfs() {
    	queue<int> q;q.push(0);
    	int tot=0;
    	while (!q.empty()) {
    		int x=q.front();q.pop();tot++;
    		for (int i=head[x];i;i=e[i].next)
    			if (!--r[e[i].to]) q.push(e[i].to);
    	}
    	return tot==sum+1;
    }
    void dfs(int x) {
    	vis[x]=f[x][1]=size[x]=1;
    	for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
    			int y=e[i].to;dfs(y);
    			for (int j=1;j<=size[x];j++) {
    				if (!f[x][j]) continue;
    				for (int k=1;k<=size[y];k++) {
    					if (!f[y][k]) continue;
    					for (int l=max(0,k-j+1);l<=k;l++)
    						(g[j+l]+=f[x][j]*f[y][k]%MOD*C[j+l-1][j-1]%MOD*C[j-1][k-l]%MOD)%=MOD;
    				}
    			}
    			size[x]+=size[e[i].to];
    			for (int i=1;i<=size[x];i++) f[x][i]=g[i],g[i]=0;
    		}
    }
    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-1]+C[i-1][j])%MOD;
    	for (int u,v,i=1;i<=m;i++) {
    		char ch[2];scanf("%d%s%d",&u,ch,&v);
    		if (ch[0]=='=') if (find(u)!=find(v)) fa[find(u)]=find(v);
    		if (ch[0]=='<') a[++c]=(data){u,v};
    	}
    	for (int i=1;i<=c;i++) {
    		int u=find(a[i].u),v=find(a[i].v);
    		link(u,v);r[v]++;
    	}
    	for (int i=1;i<=n;i++) if (fa[i]==i) {
    			sum++;
    			if (!r[i]) link(0,i),r[i]++;
    		}
    	if (!bfs()) {puts("0");return 0;}
    	dfs(0);
    	int ans=0;
    	for (int i=1;i<=n+1;i++) (ans+=f[0][i])%=MOD;
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    arm-gcc 命名规则
    Ubuntu中安装最新 Node.js 和 npm
    Tutorial: Create a Blinky ARM test project(创建一个闪灯的arm测试项目)
    Tutorial: How to install GNU MCU Eclipse?
    操作系统有关概念
    移植 uCos-III 3.03 到 STM32F429 上
    Kubernetes工作原理
    Kubernetes基础特性
    nmap详解之原理与用法
    nmap详解之基础示例
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6431918.html
Copyright © 2011-2022 走看看