CCPC 2016 G Marriage(HDU6270) 组合数学:容斥原理+NTT+启发式合并
呜呜呜,为什么我长春CCPC,知乎上出题人的题解都咕着,我写了I的题解,结果没人看呢,PTA只有4A,不会是都不补题吧T_T(其实是百度不到我)。本来想蹭一波阅读量,大失败。
咳咳,回到正题。
题目意思是,一个城市里总共有N个家庭,第i个家庭有ai个男孩和bi个女孩,已知城市里男女比例1:1,在不近亲结婚且异性恋情况下,共有多少种不同的婚配方法(有一个人配偶不同即不同)
对于每个家庭,我们可以得到至少有i对近亲配对的方法。从而得到一个生成函数。对于这n个多项式,我们乘起来就得到了,城市里至少有i对近亲结婚的方法。我们可以用容斥原理计算,这里需要注意的是,不要忘记了给剩下的人配对,即总共有N对。对至少有i对近亲结婚的方法,要乘上(N-i)!
一开始WA了好多发,后来发现。。。我优先队列没pop干净,不过我塞了n个,每次取两个再塞一个进行n-1次,为什么我只pop一个没pop干净呢?疑惑。。。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const long long MOD=998244353;
const long long g=3;
vector <long long> c[300010];
long long x1[300010],x2[300010],p[300010],inv[300010],invp[300010],rev[200010],t,sum,a[100010],b[100010];
long long ksm(long long x,long long y)
{
long long res=x;
long long ans=1;
while (y)
{
if (y&1) ans=ans*res%MOD;
res=res*res%MOD;
y=y>>1;
}
return ans;
}
struct cmp
{
bool operator()(int x, int y)
{
return c[x].size()>c[y].size();
}
};priority_queue <long long,vector<long long>,cmp> q;
void init()
{
p[0]=1;
for (int i=1;i<=300000;i++)
{
p[i]=p[i-1]*i%MOD;
}
inv[1]=1;
for(int i=2;i<=300000;i++)
{
inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
}
invp[0]=1;
invp[1]=1;
for (int i=2;i<=300000;i++)
{
invp[i]=invp[i-1]*inv[i]%MOD;
}
}
long long C(long long x,long long y)
{
return (p[x]*invp[y]%MOD)*invp[x-y]%MOD;
}
inline void NTT(long long *z,long long len,long long invv)
{
long long bit=0;
while ((1<<bit)<len)
{
++bit;
}
for(int i=0;i<=len-1;i++)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if (i<rev[i])swap(z[i],z[rev[i]]);
}
for (int mid=1;mid<len;mid*=2)
{
long long tmp=ksm(g,(MOD-1)/(mid*2));
if (invv==-1) tmp=ksm(tmp,MOD-2);
for (int i=0;i<len;i+=mid*2)
{
long long omega=1;
for (long long j=0;j<mid;++j,omega=omega*tmp%MOD)
{
long long x=z[i+j],y=omega*z[i+j+mid]%MOD;
z[i+j]=(x+y)%MOD;
z[i+j+mid]=(x-y+MOD)%MOD;
}
}
}
if (invv==-1)
for (int i=0;i<len;i++)
{
z[i]=z[i]*inv[len]%MOD;
}
}
void mul(vector<long long> &x,vector<long long> &y,vector<long long> &z)
{
long long len=1;
while (len<x.size()+y.size())
{
len=len<<1;
}
for (int i=0;i<x.size();i++)
{
x1[i]=x[i];
}
for (int i=x.size();i<len;i++)
{
x1[i]=0;
}
for (int i=0;i<y.size();i++)
{
x2[i]=y[i];
}
for (int i=y.size();i<len;i++)
{
x2[i]=0;
}
NTT(x1,len,1);
NTT(x2,len,1);
for (int i=0;i<len;i++)
{
x1[i]=x1[i]*x2[i]%MOD;
}
NTT(x1,len,-1);
for (int i=0;i<x.size()+y.size()-1;i++)
{
z.push_back(x1[i]);
}
}
int main()
{
long long t,n,i,j,flag,ans;
init();
scanf("%lld",&t);
while (t--)
{
scanf("%lld",&n);
for (i=0;i<=n*2;i++)
{
c[i].clear();
}
sum=0;
for (i=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
sum=sum+a[i];
for (j=0;j<=b[i]&&j<=a[i];j++)
{
c[i].push_back((C(a[i],j)*C(b[i],j)%MOD)*p[j]%MOD);
}
q.push(i);
}
for (i=n+1;i<n*2;i++)
{
int x=q.top();q.pop();
int y=q.top();q.pop();
mul(c[x],c[y],c[i]);
q.push(i);
}
while (!q.empty()) q.pop();
flag=1;
ans=0;
for (i=0;i<c[n*2-1].size();i++)
{
ans=ans+flag*p[sum-i]*c[n*2-1][i]%MOD;
ans=(ans+MOD)%MOD;
flag=0-flag;
}
ans=(ans+MOD)%MOD;
printf("%lld\n",ans);
}
return 0;
}
后记:明天JSCPC啦,早睡早睡。疫情之下,第一场线下赛。