囚人的旋律

题意

给你一个逆序图(令图G为有n个节点的图,编号为1~n。对于满足 1i<jn的一对ij,如果有a[i]>a[j],那么在G中编号为ij的节点之间连一条边。 得到的图G被称为逆序图。),求图G中有多少个点集既是独立点集又是覆盖集。

分析

我们可以先考虑将逆序图还原成原来的序列,然后看看独立点集和覆盖集要满足的条件是怎样。
独立点集:点集中不存在在原图中有边直接相连的点对。就是说点集中不存在序列里的逆序对,点集中的点在原序列里是一个上升序列。
覆盖集:原图中的点都在点集中或与点集中的点任意点有边直接相连。也就是所有序列中所有没选的数,都与选的数构成至少一对逆序对,即在它前面选了一个比它大的数,或在它后面选了一个比它小的数。
将两个结合起来看:若点集中在序列相邻的两个数ai,aj,(i<j,ai<aj),ak<aiak>aj,k(i,j),ai<ak<aj,k(i,j)
我们设f[i]为以第i个数为点集结尾的方案数,则f[i]=f[j]

j

现在问题变成怎么将逆序图转化成序列了。序列从左往右做,我们知道每个位置在它后面比它小的数有

x个,那么这个位置的数为当前未选的第x+1个数,证明显然。

答案为

f[i],

i满足ai>aj,j(i,n]

#include <cstdio>
#include <algorithm>
using namespace std;const int N = 1e3 + 10;
const int P = 1e9 + 7;
int a[N],c[N],f[N];
int n,m;
bool p[N];void init() {scanf("%d%d",&n,&m);for (int i = 1;i <= m;i ++) {int x,y;scanf("%d%d",&x,&y);if (x < y) c[x] ++;else c[y] ++;}
}void build() {for (int i = 1;i <= n;i ++) {c[i] ++;int j;for (j = 1;j <= n;j ++) if (!p[j]) {c[i] --;if (!c[i]) break;}a[i] = j;p[j] = true;}
}void solve() {f[0] = 1;for (int i = 1;i <= n;i ++) {int cur = -1;for (int j = i - 1;j >= 0;j --) {if (a[j] < a[i] && cur < a[j]) {f[i] = (f[i] + f[j]) % P;}if (a[j] < a[i]) cur = max(cur,a[j]); }}int ans = 0;for (int i = 1;i <= n;i ++) {int flag = 1;for (int j = i + 1;j <= n;j ++) if (a[j] > a[i]) {flag = 0;break;}ans = (ans + flag * f[i]) % P;}printf("%d",ans);
}int main() {init();build();solve();
}

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注