【问题描述】
被诅咒的监狱里流淌着囚人们的歌谣。
将罪恶的青春全部抹杀殆尽。
“看守”执掌“囚犯”的生杀大权。
“囚犯”中藏着可以杀掉“看守”的恶魔。
这就是,将人性扭曲的,“监狱游戏”。
监狱游戏的参加者被分为了看守和囚犯,两侧各 n n n人。举行监狱游戏的地点在一个被改造过了的大仓库一样的地方,里面有两排共 2 n 2n 2n个房间,服务入口侧的是囚犯的房间,行刑室侧的是看守的房间。如下图所示
相邻的看守与囚犯的房间之间可以通过对讲机互相沟通,但是声音会被处理,无法辨别。两侧的分类房中都有一排各n扇门,从左到右编号为 1 ∼ n 1 ∼ n 1∼n。进入一扇门之后会有一条狭长、黑暗,而且弯弯曲曲的走廊通向房间。由于其特殊的构造,看守的i号门对应房间未必就是囚犯的i号门对应的房间。因此,想在这个监狱游戏中胜出,了解门与门之间的对应关系是很有必要的。
接下来的问题就和监狱游戏没有太多关系了。我们令 a [ i ] a[i] a[i]表示看守的第i扇门对应囚犯的哪一扇门。令图G为有n个节点的图,编号为 1 ∼ n 1 ∼ n 1∼n。对于满足 1 ≤ i < j ≤ n 1 ≤ i <j ≤ n 1≤i<j≤n一对 i i i和 j j j,如果有 a [ i ] > a [ j ] a[i] > a[j] a[i]>a[j],那么在 G G G中编号为 i i i和 j j j的节点之间连一条边。得到的图 G G G被称为逆序图。
对于图 G = ( V , E ) G = (V, E) G=(V,E),非空点集 S ∈ V S ∈ V S∈V是一个独立集当且仅当对于任意两个点 u , v ∈ V u, v ∈ V u,v∈V,不存在 u , v ∈ E u, v ∈ E u,v∈E。而 S S S是一个覆盖集当且仅当对于任意点 v ∉ S v ∉ S v∈/S存在点 u ∈ S u ∈ S u∈S满足 u , v ∈ E u, v ∈ E u,v∈E。
我们在意的是,图 G G G中有多少个点集既是独立集又是覆盖集。出于某种不知
名的原因,被迫参加监狱游戏的大家的安危和这个问题的答案有关。拜托了,请
一定要求出这个方案数。
【输入格式】
输入第一行含有两个整数 n n n和 m m m,表示逆序图的点数和边数。
接下来 m m m行,每行描述一条边。每行包含两个 1 ∼ n 1 ∼ n 1∼n的整数,代表边的两个
端点。保证没有重边和自环。保证给定的图是一个合法的逆序图,即,存在至少一个序列,按照题目描述中所述方法得到的逆序图是给定的图。
【输出格式】
输出一个整数,表示方案数对 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007取模得到的结果。
【样例输入】
5 5
2 2
2 5
1 4
3 4
3 5
【样例输出】
3
【样例解释】
序列 2 , 4 , 5 , 1 , 3 {2,4,5,1,3} 2,4,5,1,3的逆序图即为此图。满足条件的点集有{ 1 , 2 , 3 1,2,3 1,2,3}、{ 1 , 5 1,5 1,5}和{ 4 , 5 4,5 4,5}。
可以证明不存在其他满足条件的点集。
【数据规模和约定】
对于 20%的数据, n ≤ 20 n ≤ 20 n≤20。
对于 60%的数据, n ≤ 100 n ≤ 100 n≤100。
对于 100%的数据, n ≤ 1000 n ≤ 1000 n≤1000, 0 ≤ m ≤ n ( n − 1 ) 2 0 ≤ m ≤ \frac{n(n-1)}{2} 0≤m≤2n(n−1)。
先说一句,题面有毒
然后看题,发现之前做过一道类似的,那道题的图也是一样的规则,问题是求最大独立集,显然这样就是直接一个最长上升子序列就可以了。
回到这道题,首先要求是一个最大独立集,所以肯定是一个上升序列,有要求是一个覆盖集,也就是说每一个没有选的点,必须至少和一个选了的点连边,所以这个上升序列的最后一个的右侧不能有比它大的元素,这样是不合法的,所以我们要求的是极长上升子序列
我们可以以 f [ i ] f[i] f[i]表示以 i i i结尾的方案数,如果它可以转移到 f [ j ] f[j] f[j],则需要满足两个条件,一个是 a [ j ] > a [ i ] a[j]>a[i] a[j]>a[i],另一个是 m i n { a [ k ] } > a [ j ] ( i < k < j 且 a [ k ] > a [ i ] ) min \{a[k]\} > a[j](i < k < j 且a[k]>a[i]) min{a[k]}>a[j](i<k<j且a[k]>a[i]),但是这样不太好统计答案,所以我们加一位 a [ n + 1 ] = n + 1 a[n+1]=n+1 a[n+1]=n+1,并且规定必须选它,这样答案就变成了 f [ n + 1 ] f[n+1] f[n+1]
对于把图转换成序列,可以直接用拓扑排序
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
const int mod = 1000000007;
LL n, m, ans = 0;
LL In[1010], a[1010], f[1010];
struct cmp{
bool operator()(int a, int b)
{
return a < b;
}
};
priority_queue<int, vector<int>, cmp> q;
vector<int> e[1010];
namespace file{
inline void open()
{
freopen("senritsu.in", "r", stdin);
freopen("senritsu.out", "w", stdout);
}
inline void close()
{
fclose(stdin);
fclose(stdout);
}
}
namespace input{
inline LL read()
{
LL a = 0;
LL f = 1;
char ch;
while(!((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-')));
if(ch == '-')
f = -1;
else
{
a = a * 10;
a += ch - '0';
}
while((((ch = getchar()) >= '0') && (ch <= '9')) || (ch == '-'))
{
a = a * 10;
a += ch - '0';
}
return a * f;
}
}
int main()
{
file::open();
n = input::read(), m = input::read();
for(int i = 1;i <= m;++i)
{
int x = input::read(), y = input::read();
if(x > y)
swap(x, y);
e[x].push_back(y);
++In[y];
}
for(int i = 1;i <= n;++i)
if(In[i] == 0)
q.push(i);
int Top = n;
while(!q.empty())
{
int f = q.top();q.pop();
a[f] = Top;
--Top;
int s = e[f].size();
for(int i = 0;i < s;++i)
{
int v = e[f][i];
--In[v];
if(!In[v])
q.push(v);
}
}
a[0] = 0, a[n + 1] = n + 1;
f[0] = 1;
for(int i = 0;i <= n;++i)
{
int r = n + 2;
for(int j = i + 1;j <= n + 1;++j)
{
if(a[j] < a[i] || a[j] >= r)
continue;
f[j] += f[i];
if(f[j] >= mod)
f[j] -= mod;
r = a[j];
}
}
printf("%lld\n", f[n + 1] % mod);
file::close();
return 0;
}