1. 程式人生 > >汕頭市隊賽 yyl杯1 T1

汕頭市隊賽 yyl杯1 T1

sun ddt num bind 枚舉 awr ads erp 推出

A SRM 05 - YYL 杯 R1

背景

傻逼題

描述

給一個序列,序列裏只有兩種元素1和2。現在要從序列裏選出一些非空子序列使得子序列裏兩種元素數量相同。問有多少種方案數?

輸入格式

多組數據

第一行一個正整數T,表示數據組數。

每組數據內

第一行 兩個個正整數n,表示序列的長度

第二行 n個數字,表示整個序列。

輸出格式

一個整數,表示方案數(mod 1e9+7)。

樣例輸入

1

3

2 2 1

樣例輸出

2

數據範圍與約定

技術分享

樣例解釋
在第一個樣例中,兩個子序列分別為{1,3},{2,3},集合中數字為元素下標。

這道題呢 很容易發現答案和數字的排列順序無關 我們只需要統計1和2的個數 然後一波組合數就可以推出答案了對吧 23333

但是啊 這題n可以到1e6(但是實測沒有....)所以我們不可能用o(n2)的遞推預處理組合數 而且空間也不夠

這時候我們可以想到預處理階乘 時間空間都滿足而且可以實現o(1)計算組合數的值

但是組合數C(n,m)=n!/m!(n-m)! 而除法是不滿足除法過程中取模的

這個時候逆元的派上了用場 a/b==a*power(b,P-2)%P

所以乘法是滿足取余性質的 我們就可以o(n)預處理出階乘取模後的值了

又由費馬小定理的 因為p為質數 所以我們可以推出b的逆元

只用一次快速冪就能算出1e6的逆元 然後利用公式

fac(i)=fac(i-1)*i
fac_inv(i-1)=fac_inv(i)*i

tips fac為階乘fac_inv為階乘逆元

就可以o(n)預處理逆元了

然後就枚舉一波 i—min(cnt1,cnt2) 就可以算出答案了 23333

不過註意每乘一次就要取余一次 不然可能會炸longlong

技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int mod=1e9+7,M=1000001;
LL read(){
    LL ans
=0,f=1,c=getchar(); while(c<0||c>9){if(c==-) f=-1; c=getchar();} while(c>=0&&c<=9){ans=ans*10+(c-0); c=getchar();} return ans*f; } LL T,n,cnt1,cnt2,k,ans; LL w[M],b[M]; LL qmod(LL a,LL b,LL c){ LL ans=1; while(b){ if(b&1) ans=ans*a%c; b=b/2; a=a*a%c; } return ans; } void prepare(){ int mx=1000000; w[1]=1; for(int i=2;i<=mx;i++) w[i]=w[i-1]*i%mod; b[mx]=qmod(w[mx],mod-2,mod); //printf("[%lld]\n",w[mx]); for(int i=mx;i>=1;i--) b[i-1]=b[i]*i%mod; //printf("[%d]\n",b[0]); } int main() { prepare(); T=read(); while(T--){ n=read(); cnt1=0; cnt2=0; ans=0; for(int i=1;i<=n;i++){ k=read(); if(k==1) cnt1++; else cnt2++; } for(int i=1;i<=min(cnt1,cnt2);i++) ans=(ans+w[cnt1]*b[i]%mod*b[cnt1-i]%mod*w[cnt2]%mod*b[i]%mod*b[cnt2-i]%mod)%mod; printf("%lld\n",ans); } return 0; }
View Code

汕頭市隊賽 yyl杯1 T1