A SRM 05 - YYL 杯 R1
背景
傻逼题
描述
给一个序列,序列里只有两种元素1和2。现在要从序列里选出一些非空子序列使得子序列里两种元素数量相同。问有多少种方案数?
输入格式
多组数据
第一行一个正整数T,表示数据组数。
每组数据内
第一行 两个个正整数n,表示序列的长度
第二行 n个数字,表示整个序列。
输出格式
一个整数,表示方案数(mod 1e9+7)。
样例输入
1
3
2 2 1
样例输出
2
数据范围与约定
样例解释
在第一个样例中,两个子序列分别为{1,3},{2,3},集合中数字为元素下标。
这题就用逆元求一下组合数啦
注意初始化阶乘的时候要设0的阶乘为1
然后逆元也是可以递推滴
a*x=1(mod p)
然后我们从高位的阶乘递推到低位的
a/i*x*i=1(mod p)
所以我们算一下最大的阶乘的逆元
就可以往下递推了
之所以从高位推到低位是因为这样不用考虑不整除的问题
读入优化卡到#1 !!!
#include<cstdio>
#include<cstring>
const int mod=1e9+7;
struct node
{
int a,b,min;
}e[1005];
const int N=100000;
long long aa[N+5];
long long bb[N+5];
int read()
{
int ans=0;char t=getchar();
while(t<'0'||t>'9') t=getchar();
while(t>='0'&&t<='9') ans=ans*10+t-'0',t=getchar();
return ans;
}
int gcd(int a,int b,long long int &x,long long int &y)
{
if(!b)
{
x=1,y=0;
return a;
}
int d=gcd(b,a%b,y,x);y-=x*(a/b);
return d;
}
long long calc(int n,int m)
{
long long ans=aa[n]*bb[n-m]%mod*bb[m]%mod;
return (ans+mod)%mod;
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
int n=read();int k;
int su1=0,su2=0;
for(int j=1;j<=n;j++)
{
k=read();
if(k==1) su1++;
else su2++;
}
e[i].a=su1,e[i].b=su2;
e[i].min=su1<su2?su1:su2;
}
aa[0]=1,aa[1]=1;
for(int i=2;i<=N;i++) aa[i]=aa[i-1]*i%mod;
long long x,y;
gcd(aa[N],mod,x,y);
bb[N]=x;
for(int i=N-1;i>=0;i--) bb[i]=bb[i+1]*(i+1)%mod;
for(int i=1;i<=t;i++)
{
long long ans=0;
for(int k=1;k<=e[i].min;k++)
ans=(ans+calc(e[i].a,k)*calc(e[i].b,k)%mod)%mod;
printf("%lld\n",ans);
}
return 0;
}