Codeforces Round #634 (Div. 3)

传送门

A - Candies and Two Sisters(签到)

在这里插入图片描述

#include <iostream>
using namespace std;
int main()
{
    int t,n;
    cin>>t;
    while(t--){
        cin>>n;
        if(n<=2){
            cout<<"0"<<endl;
            continue;
        }
        if(n&1) cout<<n/2<<endl;
        else cout<<n/2-1<<endl;
    }
    return 0;
}
B - Construct the String(构造)

在这里插入图片描述
1.题目大意:现在我们需要构造出一个长度为 n n n的字符串使得任意长度为 a a a的子串都只含有 b b b个不同的字符

2.一开始写尺取, W A WA WA了,找不到错误在哪。实际上先构造出一个长度为 a a a的字符串向后不断平移即可

#include <cstring>
#include <iostream>
#include <string>
using namespace std;
int t,n,a,b;
string ans;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>a>>b;
        ans="";
        for(int i=0;i<b;i++) {
            ans+='a'+i;
        }
        for(int i=b+1;i<=a;i++)
            ans+='a';
        for(int i=a+1;i<=n;i++)
            ans+=ans[i-a-1];
        cout<<ans<<endl;
    }
    return 0;
}
//尺取大法=wa
/*#include <iostream>
#include <cstring>
using namespace std;
char s[2020];
int num[30];

int main()
{
    int t,n,a,b;
    cin>>t;
    while(t--){
        cin>>n>>a>>b;
        memset(num,0,sizeof num);
        int l=0,r=0,cnt=0;
        while(r<n){
            while(r-l<a && r<n){
                if(cnt==b){
                    s[r]='a';
                    num[0]++;
                }else{
                    for(int i=0;i<26;i++)
                        if(!num[i]){
                            s[r]=i+'a';
                            num[i]++;
                            cnt++;
                            break;
                        }
                }
                r++;
            }
            if(--num[l]==0) cnt--;
            l++;
        }
        for(int i=0;i<n;i++)
            cout<<s[i];
        cout<<"\n";
    }
    return 0;
}*/
C - Two Teams Composing(二分)

在这里插入图片描述
1.题目大意:给出若干个数,从中任取,能否分成长度相同两部分使得第一部分各个数都不相同,第二部分只有一种数字。求最大分配长度

2.对于右半部分,只考虑最大数量 M A X MAX MAX的数字即可。除去最大数字设还有 m m m种数字,那么如果 M A X MAX MAX小于等于 m m m,答案就是 M A X MAX MAX。否则答案比 M A X MAX MAX小,那么答案在 [ 0 , M A X − 1 ] [0,MAX-1] [0,MAX1]的范围内,二分即可。

#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int maxn=2e5+10;
unordered_map<int,int> mp;
int a[maxn];

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t,n;
    cin>>t;
    while(t--){
        cin>>n;
        mp.clear();
        int MAX=0;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            mp[a[i]]++;
            MAX=max(mp[a[i]],MAX);
        }
        int l=0,r=MAX,ans=l,sz=mp.size();
        if(MAX<=sz-1){
            cout<<MAX<<endl;
            continue;
        }
        r--;
        while(l<=r){
            int mid=(l+r)>>1;
            if(sz>=mid){
                ans=mid;
                l=mid+1;
            }else r=mid-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}
D - Anti-Sudoku(规律)

在这里插入图片描述
1.给出一个 9 × 9 9×9 9×9的数独棋盘,而且棋盘被分成了 9 9 9个三宫格,现在要求最多改动九个数字,使得最后每一行每一列以及分成的三宫格都有两个数字相同

2.仔细观察棋盘,以及满足了每一行每一列以及三宫格元素互不相同,那么最快的解法是将整个棋盘某个数都改为另一个数,或者找到其他规律也行

#include <iostream>
using namespace std;
char a[10][10];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        for(int i=1;i<=9;i++){
            scanf("%s",a[i]+1);
        }
        for(int i=1;i<=9;i++)
            if(a[1][1]!=i+'0'){
                a[1][1]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[2][4]!=i+'0'){
                a[2][4]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[3][7]!=i+'0'){
                a[3][7]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[4][2]!=i+'0'){
                a[4][2]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[5][5]!=i+'0'){
                a[5][5]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[6][8]!=i+'0'){
                a[6][8]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[7][3]!=i+'0'){
                a[7][3]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[8][6]!=i+'0'){
                a[8][6]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++)
            if(a[9][9]!=i+'0'){
                a[9][9]=i+'0';
                break;
            }
        for(int i=1;i<=9;i++){
            printf("%s\n",a[i]+1);
        }
    }
    return 0;
}

E - Three Blocks Palindrome(双指针+前缀和)

在这里插入图片描述
在这里插入图片描述
easy version:
在这里插入图片描述
1.数据范围只有 2000 2000 2000,不难想到 O ( 26 ∗ n 2 ) O(26*n^2) O(26n2)的暴力算法。但是这样肯定解决不了 h a r d hard hard的,因此在这里能想到优化,下面的将会比较好写。

2.类似于前缀和,我们统计前缀后缀每种数出现次数, L [ i ] [ j ] L[i][j] L[i][j]表示数字 i i i在下标 j j j之前出现过多少次,同理后缀。问题就变成了,对每种数统计前缀后缀,当个数相等时,统计中间所有种类的数最多的多少种,最后取答案的最大值即可。这里还要用到双指针,尺取大法好 ,时间复杂度 O ( 26 ∗ n ) O(26*n) O(26n)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2020;
int L[30][maxn],R[30][maxn];
int a[maxn];
int t,n;

int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        memset(L,0,sizeof L);
        memset(R,0,sizeof R);
        for(int i=1;i<=n;i++){
            cin>>a[i];
            for(int j=1;j<=26;j++)
                if(j!=a[i]) L[j][i]=L[j][i-1];
                else L[j][i]=L[j][i-1]+1;
        }

        for(int i=n;i>=1;i--){
            for(int j=1;j<=26;j++)
                if(j!=a[i]) R[j][i]=R[j][i+1];
                else R[j][i]=R[j][i+1]+1;
        }

        int ans=1;
        for(int i=1;i<=26;i++){
            int l=1,r=n,res=1;
            while(l<r){  //注意临界条件是l<r不是l<=r,调一下样例就知道了
                while(L[i][l]<R[i][r]) l++;
                while(L[i][l]>R[i][r]) r--;
                if(l>=r) break;
                for(int j=1;j<=26;j++)
                    res=max(res,2*L[i][l]+L[j][r-1]-L[j][l]);
                l++,r--;
            }
            ans=max(ans,res);
        }
        cout<<ans<<endl;
    }
    return 0;
}

hard version
在这里插入图片描述
实际上如果不是内存超限,上面 e a s y easy easy原封不动的同样适用于 h a r d hard hard,因此我们要想办法优化内存,参考了 c f cf cf交的最快的几个神犇。显然我们在上面计算时只用到的是某个数出现多少次的下标,那么对于所有的数,我们可以只保存每次出现的下标,使用 v e c t o r vector vector,然后尺取的时候从最左边和最右边开始,因为下标的存储是单调的,因此优化了一个后缀数组。时间复杂度 O ( 200 ∗ n ) O(200*n) O(200n)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=2e5+10;
int a[maxn];
int sum[205][maxn];
vector<int> v[202];
int t,n;

int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=200;i++) v[i].clear();
        for(int i=1;i<=n;i++){
            cin>>a[i];
            v[a[i]].push_back(i);
            for(int j=1;j<=200;j++)
                sum[j][i]=sum[j][i-1]+(j==a[i]);
        }
        int ans=0;
        for(int i=1;i<=200;i++){
            int l=0,r=v[i].size()-1;
            ans=max(ans,(int)v[i].size());
            while(l<r){
                int L=v[i][l],R=v[i][r];
                for(int j=1;j<=200;j++){
                    ans=max(ans,2*(l+1)+sum[j][R-1]-sum[j][L]);
                }
                l++,r--;
            }
        }
        printf("%d\n",ans);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值