分类
标签
2-SAT AC自动机 BFS CDQ dfs DP fail树 FFT FFT&NTT FWT hash KD-Tree KMP LCA SPFA STL Tarjan Treap Trie 主席树 乱搞 二分 二分图匹配 二分答案 二维SPFA 交互 位运算 其他 最小生成树 分块 区间DP 半平面交 博弈论 可持久化 可持久化Trie树 后缀数组 图库 平衡树 并查集 插头DP 数学 数论 无旋Treap 日记 暴力 权值树状数组 栈 树DP 树套树 树状数组 树贪心 概率DP 模拟 欧拉定理 点分治 状压DP 生成函数 矩阵乘 线性规划 线段树 组合 网络流 群论 莫比乌斯反演 计算几何 贪心 费用流 高斯消元
980 字
5 分钟
BZOJ1004 [HNOI2008] Cards
Description
小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有种颜色:红色,蓝色,绿色.他询问 Sun 有多少种染色方案, Sun 很快就给出了答案.进一步,小春要求染出张红色,张蓝色,张绝色.他又询问有多少种方案, Sun 想了一下,又给出了正确答案. 最后小春发明了种不同的洗牌法,这里他又问 Sun 有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种. Sun 发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以的余数(为质数).
Input
第一行输入 个整数:。。
接下来 行,每行描述一种洗牌法,每行有 n 个用格隔开的整数 ,恰为 到 的一个排列,表示使用这种洗牌法,第 位变为原来的 位的牌。输入数据保证任意多次洗牌都可用这 种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。
Output
不同染法除以的余数
Sample Input
1 1 1 2 7
2 3 1
3 1 2
Sample Output
2
HINT
有 种本质上不同的染色法 和 ,使用洗牌法 一次可得 和 ,使用洗牌法 一次可得 和 。
数据满足 。
题解
一道比较基础的题目
这里先介绍一下定理
设为在置换下不变的元素的个数。 表示本质不同的方案数
则
在本题中
我们群的大小为
对于每一个置换,我们都可以求出它不变的元素个数
首先求出所有的环, 因为我们的环上必须是同一种颜色才能使他这个元素在置换下不变(这里把涂色方案看做是一个元素)
我们求出所有环之后就可以出方案的个数
!要注意如果要使所有的洗牌法构成一个群,我们必须有一个单位元, 也就是存在一个置换使得任意一个置换与他运算完不变
在本题中显然是一下的的置换, 但这个置换不是读入的, 而是要自己加上去的。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int MOD;
const int MAXN = 65;
int a[MAXN];
long long pow_mod(long long a, int b)
{
long long ans = 1;
while (b)
{
if (b & 1) ans = ans * a % MOD;
b >>= 1;
a = a * a % MOD;
}
return ans;
}
int vis[MAXN], st[MAXN];
int dfs(int x, int C)
{
if (vis[x] == C) return 0;
vis[x] = C;
return dfs(a[x], C) + 1;
}
int DP[2][25][25][25];
int main()
{
int Sr = read(), Sb = read(), Sg = read(), m = read();
MOD = read();
int n = Sr + Sb + Sg;
int Ans = 0;
memset (vis, -1, sizeof (vis));
for (int i = 0; i <= m; i++)
{
for (int j = 1; j <= n; j++)
a[j] = (i == 0 ? (j) : read());
int cnt = 0;
for (int j = 1; j <= n; j++)
if (vis[j] != i)
st[++cnt] = dfs(j, i);
memset (DP, 0, sizeof (DP));
DP[0][Sr][Sb][Sg] = 1;
int now = 0;
for (int j = 1; j <= cnt; j++)
{
now ^= 1;
memset(DP[now], 0, sizeof (DP[now]));
for (int r = 0; r <= Sr; r++)
for (int b = 0; b <= Sb; b++)
for (int g = 0; g <= Sg; g++)
{
if (r + st[j] <= Sr) DP[now][r][b][g] = (DP[now][r][b][g] + DP[now ^ 1][r + st[j]][b][g]) % MOD;
if (b + st[j] <= Sb) DP[now][r][b][g] = (DP[now][r][b][g] + DP[now ^ 1][r][b + st[j]][g]) % MOD;
if (g + st[j] <= Sg) DP[now][r][b][g] = (DP[now][r][b][g] + DP[now ^ 1][r][b][g + st[j]]) % MOD;
}
}
Ans = (DP[now][0][0][0] + Ans) % MOD;
}
printf ("%d\n", 1ll * Ans * pow_mod(m + 1, MOD - 2) % MOD);
}
BZOJ1004 [HNOI2008] Cards
https://www.nekomio.com/posts/133/