Educational Codeforces Round 102 (Rated for Div. 2) A,B,C,D题【个人题解】

A. Replacing Elements

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N];
int main()
{
    ios::sync_with_stdio(false);
    int T,n,d;
    cin>>T;
    while(T--)
    {
        cin>>n>>d;
        bool flag=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
            if(a[i]>d)flag=1;
        }
        sort(a+1,a+n+1);
        if(!flag)printf("YES\n");
        else
        {
            if(a[1]+a[2]<=d)printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}

B. String LCM

思路:找出两个字符串的最小重复子串u,并计算其重复次数分别为cnt1,cnt2,输出 l c m ( c n t 1 , c n t 2 ) lcm(cnt1,cnt2) lcm(cnt1,cnt2)个子串u即可。如果两个字符串的最小重复子串不同,则输出-1。

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int T,a[N];
string s1,s2;
int judge(string a,string b) 
// a为b的子串,b由a重复构成(若不符合条件返回-1),返回a出现次数
{
    int n1=a.length();
    int n2=b.length();
    if(n2%n1!=0)return -1;
    for(int i=0;i<n2;i+=n1)
    {
        string t=b.substr(i,n1);
        if(t!=a)return -1;
    }
    return n2/n1;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--)
    {
        cin>>s1>>s2;
        int n1=s1.length();
        string u; // s1的最小单元
        int cnt1;
        for(int i=1;i<=n1;i++)
        {
            string t=s1.substr(0,i);
            int x=judge(t,s1);
            if(x!=-1)
            {
                u=t;
                cnt1=x;
                break;
            }
        }
        int cnt2=judge(u,s2);
        if(cnt2==-1)printf("-1\n");
        else
        {
            int ans=cnt1/__gcd(cnt1,cnt2)*cnt2;
            for(int i=1;i<=ans;i++)
                printf("%s",u.c_str());
            printf("\n");
        }
    }
    return 0;
}
/*
1
ababab
abab
*/

C. No More Inversions

思路:可以先打表求一些短的序列的答案,找规律(猜结论?)。比如:
a=[1,2,3],p=[1,2,3]
a=[1,2,3,2],p=[1,3,2]
a=[1,2,3,2,1],p=[3,2,1]

a=[1,2,3,4],p=[1,2,3,4]
a=[1,2,3,4,3],p=[1,2,4,3]
a=[1,2,3,4,3,2],p=[1,4,3,2]
a=[1,2,3,4,3,2,1],p=[4,3,2,1]

这个规律你应该能看出来了吧。

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int T,n,k,a[N];
string s1,s2;
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    vector<int>ans;
    while(T--)
    {
        cin>>n>>k;
        int d=n-k;
        if(d==0)
        {
            for(int i=1;i<=k;i++)
                i==k?printf("%d\n",i):printf("%d ",i);
        }
        else
        {
            int t=k-(d+1);
            ans.clear();
            for(int i=1;i<=t;i++)
                ans.push_back(i);
            int p=k;
            for(int i=t+1;i<=k;i++)
                ans.push_back(p--);
            int sz=ans.size();
            for(int i=0;i<sz;i++)
                i==sz-1?printf("%d\n",ans[i]):printf("%d ",ans[i]);
        }
    }
    return 0;
}
/*
5
5 5
6 5
7 5
8 5
9 5
ans:
1 2 3 4 5
1 2 3 5 4
1 2 5 4 3
1 5 4 3 2
5 4 3 2 1
*/

D. Program

思路:不需要线段树,只要用数组维护一下每个位置前缀、后缀的最小、最大值即可。

假设x的所有变动值构成的序列为a,中间切一个区间[l,r],剩下左右两个区间,左区间用前缀来O(1)得到最值,右区间用后缀来O(1)得到最值。切掉[l,r]后会对右区间的最小、最大值造成影响,变动值为a[r]-a[l-1],只要减去这个变动值即可得到右区间新的最小~最大值。假设左区间最小最大分别为[low1,high1],右区间最小最大分别为[low2,high2],合并这两个区间最值即可求得答案。

另外,注意特判切掉的区间从1开始或从n结束的情况,这样就可能只剩左区间或者只剩右区间,这种情况下注意要和[0,0]进行合并(因为x初始为0)。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10,inf=0x7f7f7f7f;
char s[N];
int T,n,m,a[N],mi1[N],mx1[N],mi2[N],mx2[N];
int join(int l1,int r1,int l2,int r2) // 区间合并 [l1,r1]与[l2,r2]
{
    if(l1>l2)swap(l1,l2),swap(r1,r2);
    if(l2<=r1)return max(r1,r2)-l1+1;
    else return r1-l1+1+r2-l2+1;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>T;
    while(T--)
    {
        cin>>n>>m>>s+1;
        int x=0;
        mi1[0]=0,mx1[0]=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='+')x++;
            else x--;
            a[i]=x;
            mi1[i]=min(mi1[i-1],a[i]);
            mx1[i]=max(mx1[i-1],a[i]);
        }
        mi2[n+1]=a[n],mx2[n+1]=a[n];
        for(int i=n;i>=1;i--)
        {
            mi2[i]=min(mi2[i+1],a[i]);
            mx2[i]=max(mx2[i+1],a[i]);
        }
        int l,r,ans;
        while(m--)
        {
            cin>>l>>r;
            int low1=mi1[l-1];
            int high1=mx1[l-1];
            int low2=mi2[r+1];
            int high2=mx2[r+1];
            int d=a[r]-a[l-1]; // 切去[l,r]区间后造成的变动值
            if(l==1&&r==n)ans=1; // 全切
            else if(l==1&&r!=n)ans=join(0,0,low2-d,high2-d); // 只剩右边区间[r+1,n]
            else if(l!=1&&r==n)ans=join(0,0,low1,high1); // 只剩左边区间[1,l-1]
            else ans=join(low1,high1,low2-d,high2-d); // 切中间,剩左右两个区间
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nefu-ljw

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值