UVA11255 Necklace

6 篇文章 0 订阅

一、题目

点此看题

二、解法

母函数和 polya \text{polya} polya的结合,外面套一个 burnside \text{burnside} burnside的框框,考虑如何算不动点个数。

有一个一般形式,对于一个置换 f i f_i fi,设 m j m_j mj为循环节长度为 j j j的个数, c c c是颜色,答案的具体方案如下:
( c 1 . . + c k ) m 1 ( c 1 2 . . + c k 2 ) m 2 . . . . ( c 1 n . . + c k n ) m n (c_1..+c_k)^{m_1}(c_1^2..+c_k^2)^{m_2}....(c_1^n..+c_k^n)^{m_n} (c1..+ck)m1(c12..+ck2)m2....(c1n..+ckn)mn本题就可以用 d p dp dp算上面的柿子啦,设 d p [ i ] [ x ] [ y ] dp[i][x][y] dp[i][x][y]为前 i i i个循环节,选了 x x x a a a颜色, y y y b b b颜色, c c c颜色个数可以用总和算,就不用加入状态里面了,转移就暴力,原谅我偷个懒,不详细讲了,剩下的康康代码吧。

#include <cstdio>
#include <vector>
using namespace std;
#define int long long
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int T,n,ans,a,b,c,f[125][45][45];
vector<int> v;
int phi(int x)
{
    int r=x;
    for(int i=2;i*i<=x;i++)
        if(x%i==0)
        {
            r=r/i*(i-1);
            while(x%i==0) x/=i;
        }
    if(x>1) r=r/x*(x-1);
    return r;
}
int work()
{
    f[0][0][0]=1;
    for(int i=1,s=0;i<=v.size();i++)
    {
        int t=v[i-1];s+=t;
        for(int x=0;x<=a;x++)
            for(int y=0;y<=b;y++)
            {
                f[i][x][y]=0;
                if(x>=t) f[i][x][y]+=f[i-1][x-t][y];
                if(y>=t) f[i][x][y]+=f[i-1][x][y-t];
                if(s-x-y<=c) f[i][x][y]+=f[i-1][x][y];
            }
    }
    return f[v.size()][a][b];
}
signed main()
{
    T=read();
    while(T--)
    {
        a=read();b=read();c=read();
        n=a+b+c;ans=0;
        for(int i=1;i<=n;i++)
            if(n%i==0)
            {
                v.clear();
                for(int j=1;j<=i;j++)
                    v.push_back(n/i);
                ans+=phi(n/i)*work();
            }
        if(n&1)
        {
            v.clear();
            v.push_back(1);
            for(int i=1;i<=n/2;i++)
                v.push_back(2);
            ans+=n*work();
        }
        else
        {
            v.clear();
            for(int i=1;i<=n/2;i++)
                v.push_back(2);
            ans+=(n/2)*work();
            v.pop_back();
            v.push_back(1);
            v.push_back(1);
            ans+=(n/2)*work();
        }
        printf("%lld\n",ans/(2*n));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值