傳送門
A.Chat Group
不難發現,題目中讓我們求的式子為:
而很顯然,因為n相當的大,因此直接做的話絕對會超時,因此考慮將式子轉化。
根據組合數的公式,上述的式子可以轉化為:
因為此時的k比較小,因此我們利用組合數公式遞推求解。

#include <bits/stdc++.h> #define maxn 100005 using namespace std; typedef long long ll; ll inv[maxn+20]; const int mod=1e9+7; void init(){ inv[1]=inv[0]=1; for(int i=2;i<=maxn;i++){ inv[i]=(mod-mod/i)*inv[mod%i]%mod; } } ll powmod(ll a,ll b){ ll res=1; while(b){ if(b&1) res=res*a%mod; b>>=1; a=a*a%mod; } return res; } int main() { init(); int t,n,k,cnt=0; scanf("%d",&t); while (t--){ scanf("%d %d",&n,&k); ll res=1,y=n,sum=1; for (int i=1;i<k;i++){ res=res*y%mod*inv[i]%mod; y--; sum=(res+sum)%mod; } ll ans=(powmod(2,n)-sum+mod)%mod; printf("Case #%d: %lld\n",++cnt,ans); } return 0; }
B.Scapegoat
首先必定是每一口鍋都讓一個替罪羊去背,因為要使得最后的方差最小,因此我們考慮根據值離總平均值的距離做判斷。
因此,我們必定是要讓每一口鍋平均分配出來的值盡可能的接近總的平均值。為此,我們需要對每一口鍋分別記錄,Var1=(嚴重值-平均值)^2 以及 Var2=(加了1個人分鍋后的嚴重值-平均值)^2。而要使得在整體上答案更優,我們必定是首先選取 Var1-Var2比較大的(證明選取它能夠使得該鍋的值更接近總的平均值)。因此我們只需要用一個優先隊列進行維護,每次不斷取隊頂的鍋,將一個人分配到那口鍋即可。

#include <bits/stdc++.h> #define maxn 200005 using namespace std; double ave; struct Node{ double all,var1,var2,cur;//Var1和Var2分別代表原來的值離平均值的距離以及(Var1-加上1個人分攤后的值離平均值的距離) int num; bool operator <(const Node &b)const{//在優先隊列中維護Var2即可 return var2<b.var2; } Node(double _all,int _num,double _cur){ all=_all,num=_num,cur=_cur; var1=(cur-ave)*(cur-ave)*num; var2=var1-(all/(num+1)-ave)*(all/(num+1)-ave)*(num+1); } }; priority_queue<Node>que; int a[maxn]; int main() { int t; scanf("%d",&t); int Case=0; while(t--){ int n,m,M; scanf("%d%d",&n,&m); M=m; double sum=0; for(int i=0;i<n;i++){ scanf("%d",&a[i]); sum+=a[i]; } ave=sum/m;//計算平均值 for(int i=0;i<n;i++){ Node now=Node(a[i],1,a[i]); que.push(Node(a[i],1,a[i])); } m-=n; while(m--){ Node now=que.top(); que.pop(); que.push(Node(now.all,now.num+1,now.all/(now.num+1))); } double res=0; for(int i=0;i<n;i++){//計算方差 res+=(que.top().cur-ave)*(que.top().cur-ave)*que.top().num; que.pop(); } res/=M; printf("Case #%d: %.8f\n",++Case,res); } }
C.Traffic Light
所有紅綠燈的周期都是相同的,而且我們可以調整它的開始狀態,我們肯定是要它情況最優,最優的情況就是一直沒遇到紅燈,所以我們可以這樣調整,走到第一個紅綠燈時恰好變綠燈,然后接着走,下一個紅綠燈調整成也是剛好是變綠燈,所有的燈都這樣,這樣的情況是最優的,它對應的最差情況就是走到第一個紅綠燈的時候不是剛好變綠燈,我們模擬一下就可以知道等紅燈的時間最多就是最長的紅燈時間。

#include<bits/stdc++.h> using namespace std; const int maxn=1007; int main(){ int t,cnt=0,n; scanf("%d",&t); while (t--){ scanf("%d",&n); double x,y,sum=0,mx=0; for (int i=0;i<=n;i++) { scanf("%lf",&x); sum+=x; } for (int i=1;i<=n;i++){ scanf("%lf%lf",&x,&y); mx=max(y,mx); } sum+=mx; printf("Case #%d: %.8f\n",++cnt,sum); } return 0; }
D.Mr. Panda and Geometric Sequence
首先我們設公比為,因為要滿足一個數列為等比數列,則至少存在三個數,使得他們的公比相同,因此我們可以構造出這三個數分別為:
,
,
。
因為總的區間的上限為1e15,要滿足至少存在三個數,使得他們成等比數列,則x,y,z的值的上限必定為1e5。因此我們可以考慮分別枚舉p和q。又因為對於任意的p和q,可能又多個結果符合條件,因此我們仍然需要再枚舉一個k,繼而分別得到三個數,
,
。之后我們只需要將這三個數分別轉化成原來的數,並將這個數存在一個桶中。
因為可能存在三個以上的數的等比數列,因此我們只需要對最后一個z不斷乘上公比,並判斷是否能夠被p整除即可。
最后將桶里存的數進行排序並去重,對於每一個詢問[l,r]只需要在桶里二分找到第一個大於r,以及第一個大於l-1的坐標,兩式相減即為答案。

#include <bits/stdc++.h> using namespace std; typedef long long ll; vector<ll>vec; ll Pow[20]; int Count(ll n){//獲取位數 int cnt=0; while(n){ n/=10; cnt++; } return cnt; } void init(){ int n=1e5; Pow[0]=1; for(int i=1;i<=16;i++) Pow[i]=Pow[i-1]*10; for(int p=1;p<=n;p++){ for(int q=p+1;q<=n/p;q++){//分別枚舉p和q if(__gcd(p,q)>1) continue;//如果p和q不互質,則跳過判斷 for(int k=1;k<=n/p/q;k++){ ll x=1ll*k*p*p,y=1ll*k*p*q,z=1ll*k*q*q;//構造三個數 ll numx=Count(x),numy=Count(y),numz=Count(z);//分別獲取三個數的位數 ll numall=numx+numy+numz; if(numall>15) break;//如果總位數>15直接跳出 ll now=z,res=x*Pow[numy+numz]+y*Pow[numz]+z;//將原來的數還原,並存到桶里 vec.push_back(res); while(1){ if(now%p!=0) break;//如果之后的數不能被p整除,則跳出 //否則繼續統計答案 now=now*q/p; if(numall+Count(now)>15) break; numall+=Count(now); res=res*Pow[Count(now)]+now; vec.push_back(res); } } } } //排序並去重 sort(vec.begin(),vec.end()); vec.resize(unique(vec.begin(),vec.end())-vec.begin()); } ll Sum(ll n){//二分求下標 return upper_bound(vec.begin(),vec.end(),n)-vec.begin(); } int main() { int t; init(); scanf("%d",&t); int Case=0; while(t--){ ll l,r; scanf("%lld%lld",&l,&r); printf("Case #%d: %lld\n",++Case,Sum(r)-Sum(l-1)); } return 0; }
E.Snakebird
F.Good Number
G.Image Recognition
H.Mr. Panda and Birthday Song
首先說一下題意:
給你一個長度不大於1e6的字符串(由,'a'-'z'或‘?’組成,且‘?’可轉化為任意小寫字母),以及兩個數x,y。
現在有兩個條件:
1)字符串中存在任意一個長度為x的子串均為元音,或存在任意一個長度為y的子串均為子串。
2)字符串中存在任意一個長度為x的子串不都是元音,且存在任意一個長度為y的子串不都是輔音。
如果同時滿足條件(1)(2),則輸出SURPRISE;如果只滿足(1),則輸出(DISLIKE);如果只滿足(2),則輸出(LIKE)。
首先,對於方案(1),我們遍歷一遍整個字符串,在‘?’的條件下,默認將元音和輔音的連續數都+1,並記錄最大值判斷即可。
因此,我們只需要考慮如何處理出第二種情況。首先我們可以判斷,因為要求任意一個連續的元音/輔音小於x/y,因此我們考慮要使得他們連續的盡可能小。
因此我們考慮用dp對此進行維護。我們設dp[i][0/1],(0代表元音,1代表輔音)為位於第i號的結點時最小的連續的元音/輔音的個數。
對於某個位置的字符倘若為元音,倘若之前的狀態為連續元音,則此需要將連續元音的數量+1;而倘若之前的狀態為連續輔音,則此時需要將連續元音的數量重新置為1。
對於某個位置的字符為輔音的情況同理。
而對於某個位置為‘?’的情況如果之前的狀態為連續的元音,則此時可以將輔音清零;如果之前的狀態為連續的輔音,則此時可以將元音清零。之后只需要不斷維護他們的最小值即可。倘若發現在某一個狀態下,連續元音數超過x,連續輔音數超過y,則證明不滿足條件(2)。

#include <bits/stdc++.h> #define maxn 1000005 using namespace std; int dp[maxn][2]; char str[maxn]; int x,y; bool judge(char c){ if(c=='a'||c=='e'||c=='i'||c=='o'||c=='u') return 1; else return 0; } int main() { int t; int Case=0; scanf("%d",&t); while(t--){ scanf("%s",str); scanf("%d%d",&x,&y); int len=strlen(str); int vow=0,cons=0; int maxv=0,maxc=0; for(int i=0;i<len;i++){ if(str[i]=='?'){ vow++,cons++; maxv=max(maxv,vow); maxc=max(maxc,cons); continue; } if(judge(str[i])){ vow++; maxv=max(vow,maxv); cons=0; } else{ cons++; maxc=max(maxc,cons); vow=0; } } int flag1=0,flag2=1; if(maxv>=x||maxc>=y) flag1=1; memset(dp,0x3f3f3f3f,sizeof(dp)); dp[0][1]=dp[0][0]=0; for(int i=1;i<=len;i++){ if(str[i-1]=='?'){ if(dp[i-1][0]<x) { dp[i][0]=dp[i-1][0]+1; dp[i][1]=1; } if(dp[i-1][1]<y){ dp[i][1]=min(dp[i][1],dp[i-1][1]+1); dp[i][0]=1; } } else if(judge(str[i-1])){ if(dp[i-1][0]<x) dp[i][0]=dp[i-1][0]+1; if(dp[i-1][1]<y) dp[i][0]=1; } else{ if(dp[i-1][1]<y) dp[i][1]=dp[i-1][1]+1; if(dp[i-1][0]<x) dp[i][1]=1; } if(dp[i][0]>=x&&dp[i][1]>=y){ flag2=0; break; } } printf("Case #%d: ",++Case); if(flag1&&flag2) puts("SURPRISE"); else if(flag1) puts("DISLIKE"); else if(flag2) puts("LIKE"); } return 0; }
I.PLAYERUNKNOWN'S BATTLEGROUNDS
J.Straight Master
可以發現3,4,5可以湊出任意的3以上的數,還可以發現這個數列一直到第(n+1)位必然是升降相對應的,所以直接維護一個差分,相隔三以上的就可以正的負的相互懟掉,判斷最后所剩的是否是0就可以了

#include<bits/stdc++.h> using namespace std; const int maxn=2e5+5; int T,n; int a[maxn],b[maxn]; void read(int &ret){ ret=0; char ch=getchar(); while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9'){ ret=ret*10+ch-'0'; ch=getchar(); } } int cas; int main(){ read(T); while(T--){ read(n); for(int i=1;i<=n;i++) {read(a[i]);b[i]=a[i]-a[i-1];} b[n+1]=-a[n]; bool flag=0; if(b[2]<0||b[3]<0){printf("Case #%d: No\n",++cas);continue;} long long sum=0; for(int i=1;i<=n+1;i++){ if(b[i]>0) sum+=b[i]; int nxt=i+3; if(nxt>n+1) break; else if(b[nxt]<0) sum+=b[nxt]; if(sum<0){flag=1;break;} } if(flag||sum) printf("Case #%d: No\n",++cas); else printf("Case #%d: Yes\n",++cas); } return 0; }
K.Downgrade
溫暖的簽到題,根據題意模擬一下即可

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100005; int val[maxn]; long long sum[maxn]; int main(){ int T,cas=0; scanf("%d",&T); while(T--){ ll A,B,n; scanf("%lld%lld%lld",&A,&B,&n); for(int i=1;i<=A;i++){scanf("%d",&val[i]);sum[i]=sum[i-1]+val[i];} int m=A; while((n--)){ int tmp1,tmp2; tmp1=lower_bound(sum+1,sum+m+1,A)-sum; tmp2=A-sum[tmp1-1]; if(tmp1==A) { B=tmp2; break; } A=tmp1,B=tmp2; } printf("Case #%d: %lld-%lld\n",++cas,A,B); } return 0; }
L.SOS
想要獲勝,就要構造出S S這種情況
n為奇數時,后手構造出來是贏不了的
n為偶數時,先手構造出來是贏不了的
當n<=6時雙方都無法構造出來
當n>=7時,先手可以構造出來,必勝
當n>=16時,后手可以構造出來,必勝
其他情況為平局,雙方都無法必勝

#include <bits/stdc++.h> #define maxn 100005 using namespace std; int main() { int T,n; scanf("%d",&T); for(int cas=1;cas<=T;cas++){ scanf("%d",&n); if(n<=6)printf("Case #%d: Draw\n",cas); else if((n&1))printf("Case #%d: Panda\n",cas); else if (n>=16) printf("Case #%d: Sheep\n",cas); else printf("Case #%d: Draw\n",cas); } return 0; }
M.World Cup
溫暖的簽到題,只需要知道小組賽時有48場比賽,16強有8場比賽,8強有4場比賽,半決賽有2場比賽,決賽有1場比賽即可。
最后模擬一下就OK了。

#include<bits/stdc++.h> using namespace std; int di[5]={1,49,57,61,63}; int val[5]; int main(){ int T; long long ans; scanf("%d",&T); int cas=0; while(T--){ for(int i=0;i<5;i++) scanf("%d",&val[i]); ans=0; int n; scanf("%d",&n); for(int i=1;i<=n;i++){ int id; scanf("%d",&id); for(int j=4;j>=0;j--){ if(id>=di[j]){ ans+=val[j]; break; } } } ans*=10000; printf("Case #%d: %lld\n",++cas,ans); } return 0; }