概述
Description
有一个DAG,1号点和2号点各有一个石子.两个玩家交替将其中一个石子沿着一条有向边移动,不能移动的玩家输掉游戏.问有多少种选边的方案使得先手必胜.
Solution
将题目的条件转化成
1
1
号点和号点的SG函数不相等.
我们设
fS
f
S
表示对于点集
S
S
有多少种选边方案使得1号点和2号点
SG
S
G
函数相等.(
S
S
要么同时包含,要么同时不包含
1,2
1
,
2
)
对于当前枚举的点集
A
A
,我们枚举它的子集以及
C=∁AB
C
=
∁
A
B
,
B
B
表示SG函数为的点集,
C
C
表示SG函数非的点集.
我们考虑怎么连边:
1.
B
B
内部不能连边(一旦连边,SG函数就不等于了)
2.
C
C
内部连边方案数为,即在原来点集
C
C
的基础上,每一个点的函数都加
1
1
3. :乱连,不会对SG函数值造成影响
4.
C→B
C
→
B
:
C
C
中的每一个点至少向一个中的点连边
然后通过子集枚举来转移即可.
Code
/************************************************
* Au: Hany01
* Date: Aug 27th, 2018
* Prob: [AGC016F] Games on DAG
* Email: hany01dxx@gmail.com & hany01@foxmail.com
* Inst: Yali High School
************************************************/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define ALL(a) a.begin(), a.end()
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define count __builtin_popcount
#define y1 wozenmezhemecaia
template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
inline int read() {
static int _, __; static char c_;
for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
return _ * __;
}
const int maxn = 15, maxs = 1 << 15, MOD = 1e9 + 7;
int n, m, To[maxn], f[maxs];
inline int ad(int x, int y) { if ((x += y) >= MOD) return x - MOD; return x; }
#define chk(x) (!((x) & 3) || ((x) & 3) == 3)
int main()
{
#ifdef hany01
freopen("agc016f.in", "r", stdin);
freopen("agc016f.out", "w", stdout);
#endif
static int uu, vv;
n = read(), m = read();
For(i, 1, m) uu = read() - 1, vv = read() - 1, To[uu] |= (1 << vv);
f[0] = 1;
For(A, 1, (1 << n) - 1) if (chk(A))
for (int B = A, res, C; B; (-- B) &= A) if (chk(B)) {
res = f[C = A ^ B];
rep(i, n)
if (B >> i & 1) res = (LL)res * (1 << count(To[i] & C)) % MOD;
else if (C >> i & 1) res = (LL)res * ((1 << count(To[i] & B)) - 1) % MOD;
f[A] = ad(f[A], res);
}
printf("%dn", ad((1ll << (m >> 1)) % MOD * ((1ll << (m - (m >> 1))) % MOD) % MOD, MOD - f[(1 << n) - 1]));
return 0;
}
最后
以上就是顺心哑铃为你收集整理的【AGC016F】Games on DAG(SG函数,状压DP,子集枚举)的全部内容,希望文章能够帮你解决【AGC016F】Games on DAG(SG函数,状压DP,子集枚举)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复