A. Entertainment in MAC
這道沒細看題意,挺困的,猜了一下..
觀察到n是偶數且很大,但是樣例的長度卻沒有很長。
而且長度越長對字典序容易越大,所以猜測只複製一次。
從樣例找規律:如果字串s比翻轉後的字串s'小,則原樣輸出;否則進行翻轉,再複製一次。
程式碼:
#include<bits/stdc++.h> using namespace std; void solve(){ int n; cin>>n; string s;cin>>s; string b=s; reverse(b.begin(),b.end()); if(b<s) cout<<b+s<<endl; else cout<<s<<endl; } int main(){ int t; cin>>t; while(t--){ solve(); } }
B.Informatics in MAC
題意:問是否能把一個陣列劃分成k段,使得每段的mex值一樣。
出現mex這種特殊的值自然可以猜一猜性質。
假設每段的mex都==d,則:
1.d不能在陣列中出現
2.[0,d-1]都必須在陣列中出現
綜合以上兩點可以推出d必然是原陣列的mex。
那麼只要o(N)掃一遍,檢查當前是否湊齊了[0,d-1],沒有湊齊繼續湊;湊齊了則得到一個新的合法段。
最後如果能完整湊出來的段數<2,輸出-1;
否則對於最後一段如果不能覆蓋到n的話特殊處理一下即可
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int a[N]; void solve(){ int n; cin>>n; map<int,int>mp; for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=1; int mex=0; for(int i=0;i<=n;i++){ if(!mp[i]) { mex=i;break; } } vector<pair<int,int>>ans; mp.clear(); int cnt=0,lastl=1; for(int i=1;i<=n;i++){ if(a[i]<mex){ if(!mp[a[i]]) { mp[a[i]]=1; cnt++; } } if(cnt==mex){ // cout<<lastl<<" "<<i<<endl; ans.push_back({lastl,i}); lastl=i+1; cnt=0; mp.clear(); } } if(ans.size()<2) { cout<<-1<<endl;return; } int id=ans.size(); cout<<id<<endl; for(int i=0;i<=id-2;i++) cout<<ans[i].first<<" "<<ans[i].second<<endl; if(ans[id-1].second!=n){ cout<<ans[id-1].first<<" "<<n<<endl; } else cout<<ans[id-1].first<<" "<<ans[id-1].second<<endl; } int main(){ int t; cin>>t; while(t--){ solve(); } }
C.Messenger in MAC
這道題n的範圍<=2000,很容易想到o(n^2)的dp或者列舉答案什麼的。
cf很多c題如果是這個範圍都可以往這邊想。
首先發現一個性質:
對bi從小到大排序是最優的。
假設挑選出來的bi不是從小到大減的,也就是說不滿足b1<=b2<=b3<=b4,
那一定會出現這種情況:
發現排完序後不會變差
反而可能變優
所以考慮對bi從小到大排序。
設dp[i][j]表示強制選第i個,一共選了j個的最小花費,尋找答案時只要找到最大的 j 使得dp[_][j]<=L即可。
轉移:
最容易想到O(n^3)暴力,for i/j/k 三層迴圈下去,時間複雜度不能接受。
for(int i=1;i<=n;i++){ dp[i][1]=f[i].a; for(int j=2;j<=i;j++){ for(int k=1;k<i;k++) if(j-1<=k) { dp[i][j]=min(dp[k][j-1]+f[i].a+f[i].b-f[k].b,dp[i][j]); // cout<<i<<" "<<j<<" :"<<dp[i][j]<<endl; } } }
發現時間主要浪費在列舉i是從哪個k轉移過來的,考慮最佳化。
觀察到轉移方程可以寫作dp[k][j-1]-f[k].b + (f[i].a+f[i].b),括號裡的數為定值
則只需要對於每一個j-1,維護最小的 dp[k][j-1]-f[k].b,轉移時直接拿出來更新即可。
AC程式碼:
#include<bits/stdc++.h> using namespace std; const int N=2e3+5; #define int long long struct node{ int a,b; }f[N]; int dp[N][N],s[N]; bool cmp(node x,node y){ if(x.b==y.b) return x.a<y.a; else return x.b<y.b; } void solve(){ int n,l;cin>>n>>l; for(int i=1;i<=n;i++) cin>>f[i].a>>f[i].b; sort(f+1,f+n+1,cmp); int ans=-1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=1e15,s[j]=1e15; for(int i=1;i<=n;i++){ dp[i][1]=f[i].a; for(int j=i;j>=2;j--){ dp[i][j]=min(dp[i][j],s[j-1]+f[i].a+f[i].b); s[j]=min(s[j],dp[i][j]-f[i].b); /* for(int k=1;k<i;k++) if(j-1<=k) { dp[i][j]=min(dp[k][j-1]+f[i].a+f[i].b-f[k].b,dp[i][j]); } */ //cout<<i<<" "<<j<<" # "<<dp[i][j]<<" "<<s[j-1]<<" "<<endl; } s[1]=min(s[1],dp[i][1]-f[i].b); } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ // cout<<i<<" "<<j<<" # "<<dp[i][j]<<endl; if(dp[i][j]<=l) ans=max(ans,j); } cout<<max(ans,1ll*0)<<endl; } signed main(){ int t; cin>>t; while(t--){ solve(); } }
D.Exam in MAC
這道d最後過得比c還多..但是有點久沒寫程式碼,c調太久了,d沒過掉。
題意:給定c和集合s,求x+y不在集合s出現過,y-x(y>=x)也不在集合s出現過的(x,y)對數
如果要正解求解發現由於c的值域很大,如果要列舉x做不到,好像也沒其他好辦法
考慮正難則反
如果從[1,c]中任取兩個數,方案是C(n,2)
那麼只要減去x+y在集合中,y-x在集合中,根據容斥原理,再加上x+y且y-x都在集合中的情況。
一一討論。
1.y-x在集合中
若y-x=s[i],移項,s[i]+x=y
對x和y的限制只有 x、y<=c,顯然此時x取[1,c-s[i]]都是合法的,且x和y都是一一對應的
這樣的x個數是c-s[i]+1。
2.x+y在集合中
若x+y=s[i],則s[i]可以等於1+si-1,2+si-2...,共有s[i]/2對
3.x+y在集合中且y-x在集合中
假設x+y=s[i],y-x=s[j]
則y=(s[i]+s[j])/2,x=(s[i]-s[j])/2
為了能被2整除,s[i]和s[j]的奇偶性一定要相同,此外沒有其他限制
這部分的貢獻就是C(cnt_0,2)+C(cnt_1,2),其中cnt_0為s[i]%2==0的個數。
AC程式碼:
//搬了一個房間別人的
#include<bits/stdc++.h> #include<set> #include<queue> #include<stack> #include<algorithm> using namespace std; using ll = long long; const int NMAX = 3e5; const ll MOD = 1e9 + 7; const ll INF = 1e18 + 4; int s[NMAX+5]; int main () { ios_base::sync_with_stdio(0); cin.tie(0); int q; cin >> q; while (q--) { int n, c; cin >> n >> c; int count[2] = {0,0}; for (int i = 1; i <= n; i++) { cin >> s[i]; count[s[i]&1]++; } ll ans = 1ll * (c+1) * (c+2)/2; for (int i = 1; i <= n; i++) { ans -= s[i]/2; ans -= c - s[i] + 1; } ans += 1ll * count[0] * (count[0]-1) / 2; ans += 1ll * count[1] * (count[1]-1) / 2; count[0] = count[1] = 0; cout << ans << '\n'; } }