Codeforces Round 720 (Div. 2) A-D

liyishui發表於2024-03-06

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';
    }
    
    
}

  

相關文章