NKOJ 2640 (SDOI 2013)方程(擴展Lucas+容斥原理)

P2640【SDOI2013 R1 Day2】方程

問題描述

這裏寫圖片描述

輸入格式

輸入含有多組數據 ,第一行兩個 正整數 T,p。T表示這個測試點內的 數據 組數 ,p的含義見題目描述 。
對於每組數據,第一行 四個非負 整數 n,n1 ,n2 ,m。
第二行 n1+ n2 個正整數,表示 整數,表示 A1…An1+n2 。請注意,如果 。請注意,如果 n1+ n2 等於 0,那麼這一行將成爲空行

輸出格式

共 T行,每一個正整數表示取模後的答案

樣例輸入

3 10007
3 1 1 6
3 3
3 0 0 5
3 1 1 3
3 3

樣例輸出

3
6
0

提示

這裏寫圖片描述


考慮沒有限制的情況,那麼顯然用隔板法求解,Ans=Cm1n1
考慮大於等於的限制,那麼顯然直接m=mAi1 即可。
考慮小於等於的限制,不好做,但限制數很少,考慮容斥。
那麼Ans=Cmsumn1Xi滿+Xi滿
顯然,後面的東西也是一個隔板法,那麼剩下的問題就是算組合數。

如果p是質數,直接Lucas,而本題p可能是合數,需要用到擴展Lucas,大體思路是利用CRT解方程,
即將p分解質因數,分別對每個piai 計算答案得到一組同餘方程,然後因爲模數互質,可以用CRT合併得到最終答案,具體請百度擴展Lucas


代碼:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define N 15
#define ll long long
using namespace std;
struct node{ll a,b;};
bool operator<(node a,node b)
{
    if(a.b==b.b)return a.a<b.a;
    return a.b<b.b;
}
map<node,ll>Q;
ll T,p,A[N];
ll QM(ll a,ll b,ll c)
{
    ll o=1;
    while(b)
    {
        if(b&1)o=o*a%c;
        b>>=1;a=a*a%c;
    }
    return o;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll inv(ll a,ll b)
{
    ll x,y;exgcd(a,b,x,y);
    x=(x%b+b)%b;
    return x?x:x+b;
}
ll fac(ll n,ll px,ll pi)
{
    if(!n)return 1;
    node tmp=(node){n,px};
    if(Q[tmp])return Q[tmp];
    ll i,ans=1;
    if(n/px)
    {
        for(i=2;i<=px;i++)if(i%pi)ans=ans*i%px;
        ans=QM(ans,n/px,px);
    }
    for(i=2;i<=n%px;i++)if(i%pi)ans=ans*i%px;
    return Q[tmp]=ans*fac(n/pi,px,pi)%px;
}
ll C(ll n,ll m,ll px,ll pi)
{
    if(m>n)return 0ll;
    ll a=fac(n,px,pi),b=fac(m,px,pi),c=fac(n-m,px,pi);
    ll i,k=0,ans=0;
    for(i=n;i;i/=pi)k+=i/pi;
    for(i=m;i;i/=pi)k-=i/pi;
    for(i=n-m;i;i/=pi)k-=i/pi;
    ans=a*inv(b,px)%px*inv(c,px)%px*QM(pi,k,px)%px;
    return ans*(p/px)%p*inv(p/px,px)%p;
}
ll Cal(ll n,ll m)
{
    ll i,ans=0,x=p,px;
    for(i=2;i<=x;i++)
    {
        if(x%i)continue;px=1;
        while(x%i==0)px*=i,x/=i;
        ans=(ans+C(n,m,px,i))%p;
    }
    return ans;
}
int main()
{
    ll n,n1,n2,m,i,j,k,x,y,ans,S;
    scanf("%lld%lld",&T,&p);
    while(T--)
    {
        scanf("%lld%lld%lld%lld",&n,&n1,&n2,&m);
        for(i=1;i<=n1;i++)scanf("%lld",&A[i]);
        for(i=1;i<=n2;i++)scanf("%lld",&x),m-=x-1;
        ans=Cal(m-1,n-1);S=(1<<n1)-1;
        for(i=1;i<=S;i++)
        {
            x=0;y=m;
            for(j=1;j<=n1;j++)if(i>>j-1&1)x++,y-=A[j];
            if(x&1)ans-=Cal(y-1,n-1),ans%=p;
            else ans+=Cal(y-1,n-1),ans%=p;
        }
        printf("%lld\n",(ans%p+p)%p);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章