79跳躍遊戲 III(1306)
阿新 • • 發佈:2020-09-11
題目
正解
直接說做法了,挺好理解的。
欽定\(a\le b\)。
設\(r=x\mod (a+b)\)。
分四種情況討論:
- \(r\in [0,a-1]\)。這個情況沒有意義。
- \(r\in [a,b-1]\)。這個情況下,\(A\)能多走一步而\(B\)不能,所以只要出現了這個情況\(A\)必勝。
- \(r\in [b,2a-1]\)。這個情況下,兩個人都能各走一步。所以這個情況相當於改變先後手順序。
- \(r\in [2a,a+b-1]\)。這個情況下,\(A\)能多走至少\(2\)步,\(B\)能多走至少\(1\)步。如果\(B\)先手,相當於有個情況\(3\);如果\(A\)先手,\(A\)
可能\(b\)和\(2a\)的大小關係會有問題,但是沒有太大關係。(按照程式碼中的那樣分類就是了)
答案先乘上\(2^{cnt1}\)。
計算先手必勝和後手必勝的方案:
先手必勝:\(\sum_{i\mod 2=1} \binom{cnt3}{i}+\sum_{i\mod 2=0}\binom{cnt3}{i}cnt4\)
後手必勝:\(\sum_{i\mod 2=0} \binom{cnt3}{i}\)(這裡沒有\(cnt4\)的原因是如果有\(A\)必勝)
程式碼
using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 #define mo 1000000007 #define ll long long ll qpow(ll x,ll y=mo-2){ ll r=1; for (;y;y>>=1,x=x*x%mo) if (y&1) r=r*x%mo; return r; } ll fac[N],ifac[N]; ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;} void initC(int n){ fac[0]=1; for (int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mo; ifac[n]=qpow(fac[n]); for (int i=n-1;i>=0;--i) ifac[i]=ifac[i+1]*(i+1)%mo; } int n,a,b; int x[N]; ll ansA,ansB,ansF,ansS; void work(){ initC(n); ll cnt1=0,cnt2=0,cnt3=0,cnt4=0; for (int i=1;i<=n;++i){ int r=x[i]%(a+b); if (r<a) cnt1++; else if (r<b) cnt2++; else if (r<a*2) cnt3++; else cnt4++; } for (int i=0;i<=cnt3;++i) if (i&1) (ansF+=C(cnt3,i))%=mo; else{ (ansS+=C(cnt3,i))%=mo; (ansF+=C(cnt3,i)*cnt4)%=mo; } ll tmp=qpow(2,cnt1); (ansF*=tmp)%=mo; (ansS*=tmp)%=mo; ansA=(qpow(2,n)-ansF-ansS+mo+mo)%mo; } int main(){ // freopen("in.txt","r",stdin); freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); scanf("%d%d%d",&n,&a,&b); for (int i=1;i<=n;++i) scanf("%d",&x[i]); if (a>b){ swap(a,b); work(); swap(ansA,ansB); } else work(); printf("%lld %lld %lld %lld\n",ansA,ansB,ansF,ansS); return 0; }