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);
}

本文解析了HNOI2015实验比较问题,通过树形DP与组合数学的方法解决了基于局部图片质量比较推导全局合法序列数量的问题。详细介绍了算法流程与实现细节。
471

被折叠的 条评论
为什么被折叠?



