4013: [HNOI2015]实验比较
Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 511 Solved: 254
[ Submit][ Status][ Discuss]
Description
小
D
被邀请到实验室,做一个跟图片质量评价相关的主观实验。实验用到的图片集一共有
N
张图片,编号为
1
到
N
。实验分若干轮进行,在每轮实验中,小
D
会被要求观看某两张随机选取的图片,
然后小
D
需要根据他自己主观上的判断确定这两张图片谁好谁坏,或者这两张图片质量差不多。
用符号“
<
”、“
>
”和“
=
”表示图片
x
和
y
(
x
、
y
为图片编号)之间的比较:如果上下文中
x
和
y
是图片编号,则
x<y
表示图片
x
“质量优于”
y
,
x>y
表示图片
x
“质量差于”
y
,
x=y
表示图片
x
和
y
“质量相同”;也就是说,这种上下文中,“
<
”、“
>
”、“
=
”分别是质量优于、质量差于、质量相同的意思;在其他上下文中,这三个符号分别是小于、大于、等于的含义。图片质量比较的推理规则(在
x
和
y
是图片编号的上下文中):(
1
)
x < y
等价于
y > x
。(
2
)若
x < y
且
y = z
,则
x < z
。(
3
)若
x < y
且
x = z
,则
z < y
。(
4
)
x=y
等价于
y=x
。(
5
)若
x=y
且
y=z
,则
x=z
。
实验中,小
D
需要对一些图片对
(x, y)
,给出
x < y
或
x = y
或
x > y
的主观判断。小
D
在做完实验后,
忽然对这个基于局部比较的实验的一些全局性质产生了兴趣。在主观实验数据给定的情形下,定义这
N
张图片的一个合法质量序列为形如“
x1 R1 x2 R2 x3 R3
…
xN-1 RN-1 xN
”的串,也可看作是集合
{ xi Ri xi+1|1<=i<=N-1}
,其中
xi
为图片编号,
x1,x2,
…
,xN
两两互不相同(即不存在重复编号),
Ri
为
<
或
=
,“合法”是指这个图片质量序列与任何一对主观实验给出的判断不冲突。
例如:
质量序列
3 < 1 = 2
与主观判断“
3 > 1
,
3 = 2”冲突(因为质量序列中 3<1
且
1=2
,从而
3<2
,这与主观判断中的
3=2
冲突;同时质量序列中的
3<1
与主观判断中的
3>1
冲突)
,但与主观判断“
2 = 1
,
3 < 2”
不冲突;因此给定主观判断“
3>1
,
3=2”时,1<3=2
和
1<2=3
都是合法的质量序列,
3<1=2
和
1<2<3
都是非法的质量序列。由于实验已经做完一段时间了,小
D
已经忘了一部分主观实验的数据。对每张图片
i
,小
D
都最多只记住了某一张质量不比
i
差的另一张图片
Ki
。这些小
D
仍然记得的质量判断一共有
M
条(
0 <= M <= N
),其中第
i
条涉及的图片对为
(KXi, Xi)
,判断要么是
KXi < Xi
,要么是
KXi = Xi
,而且所有的
Xi
互不相同。小
D
打算就以这
M
条自己还记得的质量判断作为他的所有主观数据。现在,基于这些主观数据,我们希望你帮小
D
求出这
N
张图片一共有多少个不同的合法质量序列。我们规定:
如果质量序列中出现“
x = y
”,那么序列中交换
x
和
y
的位置后仍是同一个序列。
因此:
1<2=3=4<5
和
1<4=2=3<5
是同一个序列,
1 < 2 = 3
和
1 < 3 = 2
是同一个序列,而
1 < 2 < 3
与
1 < 2 = 3
是不同的序列,
1<2<3
和
2<1<3
是不同的序列。
由于合法的图片质量序列可能很多,
所以你需要输出答案对
10^9 + 7
取模的结果
Input
第一行两个正整数N,M,分别代表图片总数和小D仍然记得的判断的条数;
接下来M行,每行一条判断,每条判断形如”x < y”或者”x = y”。
Output
输出仅一行,包含一个正整数,表示合法质量序列的数目对 10^9+7取模的结果。
Sample Input
5 4
1 < 2
1 < 3
2 < 4
1 = 5
1 < 2
1 < 3
2 < 4
1 = 5
Sample Output
5
HINT
不同的合法序列共5个,如下所示:
1 = 5 < 2 < 3 < 4
1 = 5 < 2 < 4 < 3
1 = 5 < 2 < 3 = 4
1 = 5 < 3 < 2 < 4
1 = 5 < 2 = 3 < 4
100%的数据满足N<=100。
Source
题解:树形DP+组合数学
题目中说x<y或者x==y 中的y是互不相同的,那么如果根据读入的关系进行连边的话,一个y只会对应一个x,也就是只有一个前驱,这非常想树形结构。
但是题目中并没有保证不会出现环,所以我们需要特判是否存在环,如果存在则无解。
因为还有一些x==y的关系,我们用并查集维护相同的点,将所有的相等关系并到一起,然后重新建树,我们会得到一片森林,森林不好处理所以我加入一个虚点,将所有的森林穿成一颗树。
f[i][j]表示i的子树中,有j个不相同元素的方案数。
考虑如何将两个子树合并,因为子树中的元素是有序的,所有在合并的时候仍需要保证其有序性。
设两个子树的答案分别是f[x][i],f[y][j]
那么我们可以合并成的序列长度k=[max(i,j),i+j]
合并后的答案就是g[k]=f[x][i]*f[y][j]*c(i,k)*c(j-k+i,i) 因为必须保证有序性,所以我们先从空位中选择i个给第一棵子树,剩下的(k-i)个位置第二棵子树只能顺序插入,那么会有(j-k+i)个没有单独的位置,只能和第一棵子树的元素公用一个位置。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define N 503
#define p 1000000007
#define LL long long
using namespace std;
int fa[N],point[N],nxt[N],v[N],px[N],py[N];
int n,m,tot,sum[N],ins[N],vis[N];
LL f[N][N],c[N][N],g[N][N],val[N][N];
bool pd;
int find(int x)
{
if (fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
void add(int x,int y)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
//cout<<x<<" "<<y<<endl;
}
void calc(int x)
{
for (int i=0;i<=x;i++) c[0][i]=1;
for (int i=1;i<=x;i++)
for (int j=1;j<=i;j++)
c[j][i]=(c[j-1][i-1]+c[j][i-1])%p;
}
void dfs(int x)
{
bool mark=false; int mx=0;
sum[x]=1;
for (int i=point[x];i;i=nxt[i]){
dfs(v[i]);
if (mark) {
for (int k=1;k<=sum[x]+sum[v[i]];k++) g[x][k]=0;
for (int j=1;j<=sum[x]-1;j++)
for (int k=1;k<=sum[v[i]];k++)
for (int l=max(j,k);l<=j+k;l++)
g[x][l]=(g[x][l]+(val[x][j]*f[v[i]][k]%p*c[j][l]%p*c[k-l+j][j]%p)%p)%p;
for (int k=1;k<=sum[x]+sum[v[i]];k++) val[x][k]=g[x][k]%p;
}
else
for (int k=1;k<=sum[v[i]];k++) g[x][k]=val[x][k]=f[v[i]][k]%p;
sum[x]+=sum[v[i]];
mark=true;
}
for (int i=1;i<=n+1;i++)
f[x][i+1]=g[x][i]%p;
if (!mark) f[x][1]=1;
}
int main()
{
freopen("picture.in","r",stdin);
freopen("picture.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) fa[i]=i;
int cnt=0; calc(n*2);
for (int i=1;i<=m;i++) {
int x,y; char opt[2];
scanf("%d%s%d",&x,&opt,&y);
int r1=find(x); int r2=find(y);
if (opt[0]=='<') px[++cnt]=x,py[cnt]=y;
else fa[r2]=r1;
}
for (int i=1;i<=cnt;i++) {
int r1=find(px[i]); int r2=find(py[i]);
add(r1,r2); ins[r2]++; vis[r1]=1; vis[r2]=1;
}
int size=0;
for (int i=1;i<=n;i++)
if (!ins[i]&&find(i)==i&&vis[i]) {
add(n+1,i);
++size;
}
if (!size) {
printf("0\n");
return 0;
}
dfs(n+1);
LL ans=0;
for (int i=1;i<=n+1;i++)
ans=(ans+f[n+1][i])%p;
printf("%I64d\n",ans);
}