【GDOI2020模拟4.11】赢家(winner) (计数dp+容斥)

题目描述:

PinkRabbit 是一位人赢。

福州市可以抽象成一个n个点m条边的,不包含重边与自环的无向图,PinkRabbit 住在1号

点,而他的妹子住在2号点。

某一天,PinkKitten 施放了一个大魔法,让这个无向图上所有的边都变成了单向边。现在

PinkRabbit 关心的是他是否能够和他的妹子见面。

具体地,PinkRabbit 能和他的妹子见面,当且仅当存在一个点 u,满足新图上从1号点出发能够

到达u,从2号点出发也能到达 。

现在你需要计算出,在把所有 m条边进行定向的所有2^m 种方案中,有多少种方案能让

PinkRabbit 和他的妹子见面。你只需输出其对10^9+7 取模后的结果。

\(n\le 15\)

https://gmoj.net/senior/#main/show/6554

失智了,居然写了个完完全全的dp,还过了样例,然后WA0.

考虑用总方案-不合法方案。

对于不合法方案,枚举1能走到的点集是\(S\),2能走到的点集是\(T\),\(S∩T=?\)。

设\(Z=总集-S-T\)

那么\(Z\)和\(S、T\)之间的边方向确定,\(S\)和\(T\)之间不能有边。

现在就是求\(S\)的方案数(\(T\)同理)。

设\(f[S]\)表示\(S\)的方案数,同样用总-不合法。

不合法就是是\(S\)的一个子集\(S‘\),\(f[S]-=f[S‘]*(S‘和S-S‘中间的边定向)\)

预处理\(cnt[S]\)表示S内的边数就都可以快速计算了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 1e9 + 7;

const int N = 16;

int n, m, id, x, y;
int b[N][N];
int cnt[1 << 15];
ll a2[N * N];
ll f[1 << 15], g[1 << 15];

int main() {
	freopen("winner.in", "r", stdin);
	freopen("winner.out", "w", stdout);
	scanf("%d %d %d", &n, &m, &id);
	fo(i, 1, m) scanf("%d %d", &x, &y), b[x][y] = b[y][x] = 1;
	a2[0] = 1; fo(i, 1, n * n) a2[i] = a2[i - 1] * 2 % mo;
	ff(s, 0, 1 << n) {
		fo(i, 0, n - 1) if(s >> i & 1) {
			cnt[s] = cnt[s ^ (1 << i)];
			fo(j, 0, n - 1) if(s >> j & 1)
				cnt[s] += b[i + 1][j + 1];
			break;
		}
	}
	ff(s, 1, 1 << n) {
		if(s & 1) {
			f[s] = a2[cnt[s]];
			for(int t = (s - 1) & s; t > 0; t = (t - 1) & s)
				f[s] = (f[s] - f[t] * a2[cnt[s ^ t]]) % mo;
		}
		if(s & 2) {
			g[s] = a2[cnt[s]];
			for(int t = (s - 1) & s; t > 0; t = (t - 1) & s)
				g[s] = (g[s] - g[t] * a2[cnt[s ^ t]]) % mo;
		}
	}
	ll ans = a2[m];
	ff(s, 0, 1 << n) if(f[s]) {
		for(int t = s + 1; t < (1 << n); t = (t + 1) | s) if(g[s ^ t]) {
			int z = (1 << n) - 1 - t;
			if(cnt[s ^ z] + cnt[s ^ t ^ z] - cnt[z] == m) {
				ans = (ans - f[s] * g[s ^ t] % mo * a2[cnt[z]]) % mo;
			}
		}
	}
	ans = (ans % mo + mo) % mo;
	pp("%lld\n", ans);
}

原文地址:https://www.cnblogs.com/coldchair/p/12680071.html

时间: 2024-10-08 17:08:08

【GDOI2020模拟4.11】赢家(winner) (计数dp+容斥)的相关文章

【BZOJ 4665】 4665: 小w的喜糖 (DP+容斥)

4665: 小w的喜糖 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 94  Solved: 53 Description 废话不多说,反正小w要发喜糖啦!! 小w一共买了n块喜糖,发给了n个人,每个喜糖有一个种类.这时,小w突发奇想,如果这n个人相互交换手中的糖,那会有多少种方案使得每个人手中的糖的种类都与原来不同. 两个方案不同当且仅当,存在一个人,他手中的糖的种类在两个方案中不一样. Input 第一行,一个整数n 接下来n行,每行一个整数

codeforces 449D DP+容斥

Jzzhu and Numbers Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2014-07-20) Description Jzzhu have n non-negative integers a1, a2, ..., an. We will call a sequence o

bzoj 3622 DP + 容斥

LINK 题意:给出n,k,有a,b两种值,a和b间互相配对,求$a>b$的配对组数-b>a的配对组数恰好等于k的情况有多少种. 思路:粗看会想这是道容斥组合题,但关键在于如何得到每个a[i]大于b的组数. 不妨从整体去考虑,使用$f[n][j]$代表前n个中有j组$a[i]>b[i]$,很容易得到转移式$f[n][j]=f[n-1][j]+f[n-1][j-1]*(cnt[n]-(j-1))$,其中$cnt[i]$为比a[i]小的b[]个数 但是仔细思考该式子含义会发现,$f[n][j

[Luogu P1450] [HAOI2008]硬币购物 背包DP+容斥

题面 传送门:https://www.luogu.org/problemnew/show/P1450 Solution 这是一道很有意思的在背包里面做容斥的题目. 首先,我们可以很轻松地想到暴力做背包的做法. 就是对于每一次询问,我们都做一次背包. 复杂度O(tot*s*log(di)) (使用二进制背包优化) 显然会T得起飞. 接下来,我们可以换一种角度来思考这个问题. 首先,我们可以假设没有每个物品的数量的限制,那么这样就会变成一个很简单的完全背包问题. 至于完全背包怎么写,我们在这里就不做

【XSY3156】简单计数II 容斥 DP

题目大意 定义一个序列的权值为:把所有相邻的相同的数合并为一个集合后,所有集合的大小的乘积. 特别的,第一个数和最后一个数是相邻的. 现在你有 \(n\) 种数,第 \(i\) 种有 \(c_i\) 个.求所有不同的序列的权值的和. \(n\leq 50,c_i\leq 100\) 题解 考虑第一个数和最后一个数不相邻时怎么做. 记 \(g_{i,j}\) 为出现了 \(i\) 次的数分成 \(j\) 个集合,所有集合大小的乘积的和. \[ g_{i,j}=\sum_{k=1}^ig_{i-k,

codeforces 342D Xenia and Dominoes(状压dp+容斥)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud D. Xenia and Dominoes Xenia likes puzzles very much. She is especially fond of the puzzles that consist of domino pieces. Look at the picture that shows one of such puzzles. A puzzle is a 3 ×

HDU 5838 (状压DP+容斥)

Problem Mountain 题目大意 给定一张n*m的地图,由 . 和 X 组成.要求给每个点一个1~n*m的数字(每个点不同),使得编号为X的点小于其周围的点,编号为.的点至少大于一个其周围的点.   n<=5 , m<=5. 解题分析 考虑从1~n*m,从小到大依次填数,则如果某个位置编号为X且该位置还未填数,那么其周围的点均不能填数. 令dp[i][j]表示填到第i个数,状态为j . 令X的个数为cnt,那么 j ∈[ 0 , 1<<cnt). 一种情况为第i个数填在

Codeforces 611C New Year and Domino DP+容斥

"#"代表不能放骨牌的地方,"."是可以放 500*500的矩阵,q次询问 开两个dp数组,a,b,a统计横着放的方案数,b表示竖着放,然后询问时O(1)的,容斥一下, 复杂度O(n^2+q) #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<cmat

[BZOJ 1042] [HAOI2008] 硬币购物 【DP + 容斥】

题目链接:BZOJ - 1042 题目分析 首先 Orz Hzwer ,代码题解都是看的他的 blog. 这道题首先使用DP预处理,先求出,在不考虑每种硬币个数的限制的情况下,每个钱数有多少种拼凑方案. 为了避免重复的方案被转移,所以我们以硬币种类为第一层循环,这样阶段性的增加硬币. 一定要注意这个第一层循环要是硬币种类,并且初始 f[0] = 1. f[0] = 1; for (int i = 1; i <= 4; ++i) { for (int j = B[i]; j <= MaxS; +