1. 程式人生 > 實用技巧 >十四、nginx的負載均衡

十四、nginx的負載均衡

題目


正解

直接說做法了,挺好理解的。
欽定\(a\le b\)
\(r=x\mod (a+b)\)
分四種情況討論:

  1. \(r\in [0,a-1]\)。這個情況沒有意義。
  2. \(r\in [a,b-1]\)。這個情況下,\(A\)能多走一步而\(B\)不能,所以只要出現了這個情況\(A\)必勝。
  3. \(r\in [b,2a-1]\)。這個情況下,兩個人都能各走一步。所以這個情況相當於改變先後手順序。
  4. \(r\in [2a,a+b-1]\)。這個情況下,\(A\)能多走至少\(2\)步,\(B\)能多走至少\(1\)步。如果\(B\)先手,相當於有個情況\(3\);如果\(A\)先手,\(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;
}