【BZOJ】1003 Cards

【解析】Burnside引理+背包dp+乘法逆元

[Analysis]

这道题卡了好久,就是没想懂置换跟着色是不一样的。

根据burnside引理,在一个置换群作用下不等价类的个数为每个置换作用下不动点个数的平均数。

在这道题中:

置换的对象 ——
每个状态,标号为1—N(这里的N不是题目的N,而是状态的个数)。

不动点 ——
前后染色状态完全相同的状态的个数。

所以就是求经过变换后前后状态完全相同的个数。

[Sum]

Burnside引理几个注意的地方

[1]什么是Burnside引理?

[2]置换的对象是什么?

[3]不动点意味着什么?

[4]着色变换不是置换。

[Code]

<span style="font-size:18px;">/**************************************************************
    Problem: 1004
    User: y20070316
    Language: C++
    Result: Accepted
    Time:328 ms
    Memory:2164 kb
****************************************************************/

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

const int N=70;

int Sr,Sb,Sg,n,m,p; //Basic
int map[N][N],d[N],v[N],cnt;    //Substitution
int f[N][N][N]; //Damatic Programming
int res;    //Answer

inline int read(void)
{
    int s=0,f=1; char c=getchar();
    for (;c<'0'||c>'9';c=getchar());
    for (;'0'<=c&&c<='9';c=getchar()) s=s*10+c-'0';
    return s*f;
}

void init(void)
{
    Sr=read(),Sb=read(),Sg=read(),n=Sr+Sb+Sg;
    m=read(),p=read();
    for (int i=1;i<=m;i++)
        for (int j=1;j<=n;j++) map[i][j]=read();
    m++; for (int j=1;j<=n;j++) map[m][j]=j;
}

void work(void)
{
    for (int i=1;i<=m;i++)
    {
        cnt=0; memset(v,0,sizeof v);
        memset(d,0,sizeof d);

        for (int j=1;j<=n;j++)
            if (!v[j])
            {
                v[j]=d[++cnt]=1;
                for (int k=map[i][j];k^j;k=map[i][k])
                    v[k]=1,d[cnt]++;
            }

        memset(f,0,sizeof f); f[0][0][0]=1;
        for (int i=1;i<=cnt;i++)
            for (int j=Sr;j>=0;j--)
                for (int k=Sb;k>=0;k--)
                    for (int q=Sg;q>=0;q--)
                    {
                        f[j][k][q]=0;
                        if (j>=d[i]) f[j][k][q]=(f[j][k][q]+f[j-d[i]][k][q])%p;
                        if (k>=d[i]) f[j][k][q]=(f[j][k][q]+f[j][k-d[i]][q])%p;
                        if (q>=d[i]) f[j][k][q]=(f[j][k][q]+f[j][k][q-d[i]])%p;
                    }
        res=(res+f[Sr][Sb][Sg])%p;
    }
}

int mi(int i,int j)
{
    if (!j) return 1;
    int s=mi(i,j>>1);
    s=s*s%p;
    if (j&1) s=s*i%p;
    return s;
}

void print(void)
{
    int inv=mi(m,p-2);
    res=res*inv%p;
    printf("%d\n",(res+p)%p);
}

int main(void)
{
    init();
    work();
    print();

    return 0;
}</span>

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 08:18:48

【BZOJ】1003 Cards的相关文章

【BZOJ】3319: 黑白树

http://www.lydsy.com/JudgeOnline/problem.php?id=3319 题意:给一棵n节点的树(n<=1e6),m个操作(m<=1e6),每次操作有两种:1.查询u到根的第一条黑边的编号.2.将u到v的路径全部染成黑色 #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream>

【bzoj】4538: [Hnoi2016]网络

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4538 维护一个数据结构支持对于一颗树的操作,需要支持: 1.对于树上的一条路径上的每个点上放一个值. 2.撤销某次操作的路劲放. 3.查询除了经过这个点的路径的最大值. 往一个路径上丢值相当于往不经过条路径的所有点上丢值. 用一个树链剖分即可维护,对于操作区间取反. 直接查询单点最大值即可. 为了维护单点最大值,线段树中的每一个点对应两个堆,用于维护插入誉删除. 防止爆空间,所以标记永久

【BZOJ】1821: [JSOI2010]Group 部落划分 Group(最小生成树+贪心)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=1821 这题裸题. 本题要求最短距离最长,很明显,我们排序. 这里存在贪心,即我们把边权最小的全分给n个部落的内部,然后剩下的边最小的就是答案. 将边权较小的边分给k个部落,用并查集生成最小树,使得内部的边总是小于连到外部的边.然后分剩下k个点即可,剩下的k个点的那条边一定是部落之间最小的且最长的边. #include <cstdio> #include <cstring> #

【BZOJ】【3083】遥远的国度

树链剖分/dfs序 其实过了[BZOJ][4034][HAOI2015]T2以后就好搞了…… 链修改+子树查询+换根 其实静态树的换根直接树链剖分就可以搞了…… 因为其实只有一样变了:子树 如果root在x的子树中(以1为根dfs的时候),那么现在x的子树就变成了整个dfs序中,除去含有root的那个子树的剩下的部分,画个图大概就是这样:(红色部分为现在的子树) 我们发现,这种子树由于换根而产生变化的情况,仅当在以1为根时的树中,x是new_root的祖先时发生,那么我们判断这种情况是否发生只需

【BZOJ】2301: [HAOI2011]Problem b(莫比乌斯+分块)

http://www.lydsy.com/JudgeOnline/problem.php?id=2301 和这题不是差不多的嘛--[BZOJ]1101: [POI2007]Zap(莫比乌斯+分块) 唯一不同的地方是这题有下界.. 下界除以k的时候取上界,然后分块的时候因为有4个数,所以要分成4块来搞.. 然后就行了.. #include <cstdio> #include <cstring> #include <cmath> #include <string>

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

最大权闭合图 &amp;&amp; 【BZOJ】1497: [NOI2006]最大获利

最大权闭合图详细请看胡伯涛论文<最小割模型在信息学竞赛中的应用>,我在这里截图它的定义以及一些东西. 假设我们有一个图,点集的出边都是连到点集的,那么称这个为闭合图.现在这些点集都有个权值,我们要选择某个闭合图使得权值最大. 回到此题: 最大获利这一题,我们可以这样看,用户群和中转站为带权的点集,用户群的权为收益,中转站的权为负的成本,即0-成本,用户群向其中两个中转站连弧,那么这个就是一个闭合图. 我们要求这个闭合图的权值和最大,即最大收益,那么就能转移到上面的求最大权闭合图的做法去了. 做

【BZOJ】1012: [JSOI2008]最大数maxnumber(树状数组+区间最值)

http://www.lydsy.com/JudgeOnline/problem.php?id=1012 树状数组原来我只懂得sum和add的操作,今天才知道可以有求区间最值的操作,我学习了一下写了个,1a了. 区间最值其实和区间求和差不多,就是将sum数组的含义转移到max,然后通过特定的区间更新max. 在区间求和中,当我们维护max[i]的时候,要找到它前面所有的max[j]来更新,在这里因为是树状数组,所以可以降成一个log级,画图可知,max[i]需要的max只有max[i-2^0],

【BZOJ】3052: [wc2013]糖果公园

http://www.lydsy.com/JudgeOnline/problem.php?id=3052 题意:n个带颜色的点(m种),q次询问,每次询问x到y的路径上sum{w[次数]*v[颜色]},可以单点修改颜色.(n, m, q<=100000) #include <bits/stdc++.h> using namespace std; const int N=100005, M=100005; typedef long long ll; inline int getint()