Codeforces Round 932 (Div. 2)A-D

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

  

 

posted @ 2024-03-06 01:16  liyishui  阅读(148)  评论(0编辑  收藏  举报