bzoj 2445 最大团 CRT 组合数取模

博客探讨了如何计算n个点对称标记的最大团数量,并将结果取m次幂。通过将模数分解质因数,使用中国剩余定理(CRT)进行合并,解决了这个问题。虽然不能直接应用Lucas定理,但可以通过预处理阶乘并采用类似于处理礼物的方法,尽管效率稍低,但在本题中仍能快速得出答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意太迷。。。
其实就是求n个点symmetric labeled cliquer的个数,然后取一个m次幂。
答案就是

mx|nn!(x!)nx(nx!)

把mod-1分解质因数,把这个数分别取模mod-1的因子,然后用CRT合并。

不过这个东西不是组合数,不能用lucas。可以先预处理阶乘,用礼物的方法做,会比lucas多个log,不过这题还挺快的。

#include <bits/stdc++.h>
using namespace std;
#define mod 999999599
#define ll long long
#define PA pair<int,int> 
#define N 8100
int p[5]={2,13,5281,7283};
int n,m,T;
int a[5],jc[5][N];
int qpow(int x,int y,int md)
{
    int ret=1;
    while(y)
    {
        if(y&1)ret=(ll)ret*x%md;
        x=(ll)x*x%md;y>>=1;
    }
    return ret;
}
void init()
{
    for(int i=0;i<4;i++)
    {
        jc[i][0]=1;
        for(int j=1;j<p[i];j++)
            jc[i][j]=jc[i][j-1]*j%p[i];
    }
}
void exgcd(ll &x,ll &y,int a,int b)
{
    if(b==0){x=1;y=0;return;}
    exgcd(y,x,b,a%b);y-=a/b*x;
}
PA get(int x,int tp,int md)
{
    if(x==0)return make_pair(1,0);
    int t=x/md;
    PA r1=get(t,tp,md);
    return make_pair(qpow(jc[tp][md-1],t,md)*
    jc[tp][x-t*md]%md*r1.first%md,r1.second+t);
}
void cal(int x)
{
    for(int i=0;i<4;i++)
    {
        PA t1=get(n,i,p[i]),t2=get(x,i,p[i]),t3=get(n/x,i,p[i]);
        if(t1.second-t2.second*(n/x)-t3.second)continue;
        a[i]=(a[i]+t1.first*qpow(qpow(t2.first,n/x,p[i])*t3.first%p[i],p[i]-2,p[i])%p[i])%p[i];
    }
}
int CRT(int md)
{
    int ret=0;
    ll x,y;
    for(int i=0;i<4;i++)
    {
        exgcd(x,y,md/p[i],p[i]);
        x=(x%p[i]+p[i])%p[i];
        ret=(ret+md/p[i]*x%md*a[i]%md)%md;
    }
    return ret;
}
int main()
{   
    //freopen("tt.in","r",stdin);
    init();
    scanf("%d",&T);
    while(T--)
    {   
        memset(a,0,sizeof(a));
        scanf("%d%d",&n,&m);
        for(int i=1;(ll)i*i<=n;i++)
            if(n%i==0)
            {
                cal(i);
                if(i*i!=n)cal(n/i);
            }
        printf("%d\n",qpow(m,CRT(mod-1),mod));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值