题目链接: http://acm.whu.edu.cn/land/problem/detail?problem_id=1553
题目大意:给定N个球,M个标记,每个球可以任意打一种标记,给定P对关系,P<=15,要求每对关系A,B球标记不能相同,询问方案数
思路:容斥原理。答案是总方案数M^N - P对中有1对球标记相同的情况 + 2对球标记相同的情况 ... ...
具体用二进制模拟满足哪几对关系相同的情况,标记之间的互相影响用并查集统计最后标记不同的情况数。
Code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define foru(i, a, b) for (int i=a; i<=b; i++)
#define ford(i, a, b) for (int i=a; i>=b; i--)
#define ll long long
#define N 200
#define M 1000000007
ll ans;
ll n, m;
ll dx[N];
int fa[N];
int p, a[N], b[N];
ll g(ll a, ll b)
{
ll tmp = 1;
while (b > 0)
{
if ((b & 1) == 1)
tmp = (tmp * a) % M;
b >>= 1;
a = (a * a) % M;
}
return tmp;
}
int getfa(int x)
{
if (fa[x] == x)
return x;
fa[x] = getfa(fa[x]);
return fa[x];
}
void mul(int x, int y)
{
int fx = getfa(x);
int fy = getfa(y);
if (fx != fy)
fa[fx] = fy;
}
void solve()
{
ans = g(m, n);
foru(i, 1, dx[p + 1] - 1)
{
ll k = 0ll, t = 0ll;
foru(j, 1, n) fa[j] = j;
foru(j, 1, p) if ((i & dx[j]) == dx[j])
{
k += 1ll;
mul(a[j], b[j]);
}
foru(j, 1, n) if (fa[j] == j)
t += 1ll;
if ((k & 1) == 1)
k = -1ll;
else
k = 1ll;
ans = (ans + k * g(m, t)) % M;
while (ans < 0) ans += M; // 加回正值
}
printf("%lld\n", ans);
}
int main()
{
//freopen("G.txt", "r", stdin);
dx[1] = 1ll;
foru(i, 2, 16) dx[i] = dx[i - 1] << 1;
while (scanf("%lld %lld", &n, &m) != EOF)
{
scanf("%d", &p);
foru(i, 1, p) scanf("%d %d", &a[i], &b[i]);
solve();
}
return 0;
}
Tips:
1° 荣斥的过程中有减法 最后计算答案如果是负数要加回正值