题意
给你一个逆序图(令图G为有n个节点的图,编号为1~n。对于满足
1≤i<j≤n
的一对
i
和
分析
我们可以先考虑将逆序图还原成原来的序列,然后看看独立点集和覆盖集要满足的条件是怎样。
独立点集:点集中不存在在原图中有边直接相连的点对。就是说点集中不存在序列里的逆序对,点集中的点在原序列里是一个上升序列。
覆盖集:原图中的点都在点集中或与点集中的点任意点有边直接相连。也就是所有序列中所有没选的数,都与选的数构成至少一对逆序对,即在它前面选了一个比它大的数,或在它后面选了一个比它小的数。
将两个结合起来看:若点集中在序列相邻的两个数
ai,aj,(i<j,ai<aj),有∀ak<ai或ak>aj,k∈(i,j),即不存在ai<ak<aj,k∈(i,j)
我们设
f[i]
为以第
i
个数为点集结尾的方案数,则
j满足上述条件。
现在问题变成怎么将逆序图转化成序列了。序列从左往右做,我们知道每个位置在它后面比它小的数有 x 个,那么这个位置的数为当前未选的第
答案为 ∑f[i] , i 满足
#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();
}