zoukankan      html  css  js  c++  java
  • 【BZOJ】1004: [HNOI2008]Cards(置换群+polya+burnside)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1004

    学习了下polya计数和burnside引理,最好的资料就是:《Pólya 计数法的应用》 --陈瑜希

    burnside:

    $$等价类的个数=frac{1}{|G|}sum_{i=1}^{s}D(a_i), a_i in G$$其中$D(a_i)=a_i置换中染色后不变的方案$

    而polya:

    $$D(a_i)=k^{C(a_i)},其中C(a_i)是a_i的循环节个数$$证明很简单,要让染色不变,那么每个循环节的颜色一定要一样。

    但是在这一题,颜色数目不是无限的,那么我们可以考虑DP

    对于每个置换$a_i$,有循环节$cnt$个,每个循环节有$s[x]$个元素,那么我们用背包思想计数即可,设f[r,b,g]表示1~i的循环节用了r个红色,b个蓝色,g个绿色,有

    f[r,b,g]=f[r-s[i], b, g]+f[r, b-s[i], g]+f[r, b, g-s[i]]

    然后注意,因为是置换群,所以要满足群的定义,即题目虽然满足了逆元“对每种洗牌法,都存在一种洗牌法使得能回到原状态”,而封闭性和结合律只要是置换群都满足,所以还缺一个单位元的性质,因此我们要将单位元加进去再计数

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    
    const int N=65;
    int a[N], s[N], vis[N], R, B, G, n, m, f[22][22][22], p, ans;
    void add(int &a, const int &b) { a=(a+b)%p; a=(a+p)%p; }
    int mpow(int a, int b) {
    	int ret=1;
    	for(; b; a=(a*a)%p, b>>=1) if(b&1) ret=(ret*a)%p;
    	return ret;
    }
    int getans() {
    	CC(f, 0); CC(s, 0); CC(vis, 0); int cnt=0;
    	for1(i, 1, n) if(!vis[i]) { ++cnt; for(int x=i; !vis[x]; x=a[x]) vis[x]=1, ++s[cnt]; }
    	f[0][0][0]=1;
    	for1(i, 1, cnt) for3(r, R, 0) for3(b, B, 0) for3(g, G, 0) {
    		if(r-s[i]>=0) add(f[r][b][g], f[r-s[i]][b][g]);
    		if(b-s[i]>=0) add(f[r][b][g], f[r][b-s[i]][g]);
    		if(g-s[i]>=0) add(f[r][b][g], f[r][b][g-s[i]]);
    	}
    	return f[R][B][G];
    }
    int main() {
    	read(R); read(B); read(G); read(m); read(p); n=R+B+G;
    	for1(i, 1, m) { for1(i, 1, n) read(a[i]); add(ans, getans()); }
    	for1(i, 1, n) a[i]=i;
    	add(ans, getans());
    	ans*=mpow(m+1, p-2);
    	printf("%d
    ", ans%p);
    	return 0;
    }
    

      


    Description

    小春现在很清闲,面对书桌上的N张牌,他决定给每张染色,目前小春只有3种颜色:红色,蓝色,绿色.他询问Sun有多少种染色方案,Sun很快就给出了答案.进一步,小春要求染出Sr张红色,Sb张蓝色,Sg张绝色.他又询问有多少种方案,Sun想了一下,又给出了正确答案. 最后小春发明了M种不同的洗牌法,这里他又问Sun有多少种不同的染色方案.两种染色方法相同当且仅当其中一种可以通过任意的洗牌法(即可以使用多种洗牌法,而每种方法可以使用多次)洗成另一种.Sun发现这个问题有点难度,决定交给你,答案可能很大,只要求出答案除以P的余数(P为质数).

    Input

    第一行输入 5 个整数:Sr,Sb,Sg,m,p(m<=60,m+1<p<100)。n=Sr+Sb+Sg。接下来 m 行,每行描述
    一种洗牌法,每行有 n 个用空格隔开的整数 X1X2...Xn,恰为 1 到 n 的一个排列,表示使用这种洗牌法,
    第 i位变为原来的 Xi位的牌。输入数据保证任意多次洗牌都可用这 m种洗牌法中的一种代替,且对每种
    洗牌法,都存在一种洗牌法使得能回到原状态。

    Output

    不同染法除以P的余数

    Sample Input

    1 1 1 2 7
    2 3 1
    3 1 2

    Sample Output

    2

    HINT

    有2 种本质上不同的染色法RGB 和RBG,使用洗牌法231 一次可得GBR 和BGR,使用洗牌法312 一次 可得BRG 和GRB。

    100%数据满足 Max{Sr,Sb,Sg}<=20。

    Source

     
  • 相关阅读:
    Swift学习笔记(7)--控制流
    安装APK时报 Installation failed with message Failed to finalize session : INSTALL_FAILED_USER_RESTRICTED: Invalid apk.
    Android Notification 的四种使用方式
    Socket.io
    socket
    socket.io 中文手册 socket.io 中文文档
    Android中的CardView使用
    TabLayout实现底部导航栏(2)
    使用PagerSlidingTabStrip实现顶部导航栏
    TabLayout实现顶部导航栏(1)
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4114956.html
Copyright © 2011-2022 走看看