Codeforces Round #658 (Div. 1) / contest 1381


A1A2BCDE

( √:做出; ●:尝试未做出; ○:已补题 )


题目地址:https://codeforces.com/contest/1381

第一次打 div1,然后一下子rating就掉了七十多,其实A2的思路还是很简单的,可是当时想复杂了,然后C题很可惜没有做出来。感觉最近几次 cf 的比赛以及牛客多校的比赛发挥的都不是很好,这是为什么呢?



A1 Prefix Flip (Easy Version)

题意:给定一个01字符串,每次可以选择一个 x,然后操作。操作方法为把该字符串的 x 前缀全部反转(0变成1, 1变成0),然后翻转(首尾交换)。目标是使该字符串变成另外一个01字符串。要求给出翻转方案。字符串的长度为 n( n ≤ 1000 n\le 1000 n1000),操作次数不超过 3n 。

思路:很明显是要从后往前满足。考虑第 i 个位置,如果已经一样了就可以不用管,如果不一样那么它将会由第一个位置得到。所以如果第一个位置和当前一样,首先要操作前缀 1 使得第一个和点前 i 的不一样,然后再操作前缀 i 就可以满足 i 了。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
int a[maxn],b[maxn];
char s[maxn],t[maxn];

int main()
{
    int T=read();
    while(T--)
    {
        int n=read();
        scanf("%s %s",s+1,t+1);
        REP(i,1,n) a[i]=s[i]-'0',b[i]=t[i]-'0';
        VI ans;
        REP_(i,n,1)
        {
            if(a[i]==b[i]) continue;
            if(a[1]==b[i]) a[1]=a[1]^1,ans.pb(1);
            ans.pb(i);
            REP(j,1,i) a[j]^=1;
            reverse(a+1,a+i+1);
        }
        printf("%d ",ans.size());
        for(int i:ans) printf("%d ",i);
        puts("");
    }

    return 0;
}



A2 Prefix Flip (Hard Version)

题意:在第一题的基础上变成 n ≤ 1 e 5 n\le 1e5 n1e5 ,然后操作次数不超过 2n 。

思路:比赛的时候我就一直按照 A 的思路来想,然后一直在考虑用平衡树维护区间翻转, 用线段树维护每个值反转了多少次,然后就GG了。其实对于每个位置 i,不管它是否已经满足,我们都可以固定地操作前缀 i ,这样保证每次第 i 个位置都由第一个来满足,而且这种情况下第一个位置在原来数组的位置是 1,n,2,n-1,... 很方便计算。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=2e5+5;
char s[maxn],t[maxn];
int a[maxn],b[maxn];

int main()
{
    int T=read();
    while(T--)
    {
        int n=read();
        scanf("%s %s",s+1,t+1);
        REP(i,1,n) a[i]=s[i]-'0',b[i]=t[i]-'0';
        VI ans;
        REP(i,1,n)
        {
            int x=i&1?(i+1)/2:n-i/2+1;
            int y=i&1?a[x]:a[x]^1;
            if(b[n-i+1]==y) ans.pb(1);
            ans.pb(n-i+1);
        }
        printf("%d ",ans.size());
        for(int i:ans) printf("%d ",i);
        puts("");
    }

    return 0;
}



B Unmerge

题意:给定两个数组 a 和 b,然后 merge(a, b) 操作表示:如果 a 1 < b 1 a_1<b_1 a1<b1 ,那么把 a 1 a_1 a1 拿出来,然后继续 merge(a[2,…], b);如果 a 1 > b 1 a_1>b_1 a1>b1 则相应的把 b 1 b_1 b1 拿出来,然后继续 merge(a, b[2,…]) 。现在给出一个长度为 2n 的全排列 p,问是否存在两个长度都为 n 的数组 a 和 b,使得 merge(a, b) = p 。

思路:我们先考虑 p 中 2n 所在的位置,然后可以发现这个位置以及之后的所有元素,都必须在 a 或 b 中某一个的末尾,然后再考虑当前还没被选中的最大的数的位置,然后它以及之后的所有没被选中的元素,都必须添加至 a 或 b 中某一个的前面,这样考虑下去,思路就很明显了:每次找到当前最大的数,然后把它以及之后的所有没被选中的元素分成一组,这样就得到了若干组,如果这若干个数可以选出某一些,使得它们的和为 n ,那么就存在方案。

比如所对于样例:3 2 6 1 5 7 8 4,分完组之后为 8 476 1 53 2,可以选出若干个组使得和为 4,所以存在方案。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=4005;
int n,a[maxn],vis[maxn],w[maxn],b[maxn],cnt;
int can[maxn];

int main()
{
    int T=read();
    while(T--)
    {
        mem(vis,0); cnt=0; mem(can,0);
        n=read();
        REP(i,1,n<<1) a[i]=read(),w[a[i]]=i;
        int last=n<<1;
        REP_(i,n<<1,1) if(!vis[i])
        {
            int x=w[i],tot=0;
            REP(j,x,last) tot++,vis[a[j]]=1;
            last=x-1;
            b[++cnt]=tot;
        }
        sort(b+1,b+cnt+1);
        can[0]=1;
        REP(i,1,cnt)
        {
            for(int j=n-b[i];j>=0;j--) if(can[j])
                can[j+b[i]]=1;
        }
        puts(can[n]?"YES":"NO");
    }

    return 0;
}



C Mastermind

题意:给出两个数组 a 和 b,定义 x 表示它们中 a i = b i a_i=b_i ai=bi 的位置个数,定义 y 表示 a 和 b 中相同元素的个数,比如对于 a: 3 1 6 1 2b: 3 1 1 2 5,那么它们的 x 和 y 分别是 2 和 4 。现在给出一个数组 a,给出 x 和 y,让你找一个满足要求的 b,或者判断不可能找到。其中 a 的所有元素大小处于区间 [1, n+1] 。

思路:首先在 [1, n+1] 中肯定存在一个 k 从没出现过,这个要好好利用。我们肯定优先选择出现次数多的,让他们对 x 贡献,因为如果最后剩下的某个数出现次数特别多,那么我们很难实现 a i ≠ b i a_i\neq b_i ai=bi 。选完 x 个固定它们之后,剩下的 n-x 个数,可以让它们按照原来的位置顺序平移 (n-x)/2 ,这么做是尽可能地让它们的新位置的值和原来位置的不一样,也就是说使得剩下的 n-x 个位置 a i = b i a_i=b_i ai=bi 的个数尽可能少。最后我们要把 n-y 个位置的值变成 k,这样保证 y 的意义,在变的时候优先变那些未固定的 a i = b i a_i=b_i ai=bi 的位置,然后剩下的就随便变了。

最后根据 x 的意义检查(因为最后变的时候可能变不完所有 a i = b i a_i=b_i ai=bi 的位置)是否满足要求。

代码

#define DIN freopen("input.txt","r",stdin);
#define DOUT freopen("output.txt","w",stdout);
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> P;
int read()
{
    int x=0,flag=1; char c=getchar();
    while((c>'9' || c<'0') && c!='-') c=getchar();
    if(c=='-') flag=0,c=getchar();
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return flag?x:-x;
}

const int maxn=1e5+5;
int n,a[maxn],num[maxn],xuan[maxn],po[maxn],ans[maxn];
VI w[maxn];

int main()
{
    int T=read();
    while(T--)
    {
        n=read();
        REP(i,1,n+1) num[i]=xuan[i]=po[i]=ans[i]=0,w[i].clear();
        int x=read(),y=read();
        REP(i,1,n) a[i]=read(),num[a[i]]++,w[a[i]].pb(i);
        int k=1;
        while(num[k]) k++;

        priority_queue<P> Q;
        REP(i,1,n+1) if(num[i]) Q.push(P(num[i],i));
        REP(i,1,x)
        {
            P p=Q.top(); Q.pop();
            int t=p.second;
            num[t]--;
            xuan[w[t][po[t]]]=1;
            ans[w[t][po[t]++]]=t;
            if(num[t]) Q.push(P(num[t],t));
        }

        VI temp;
        REP(i,1,n+1) if(num[i]) while(num[i]--) temp.pb(w[i][po[i]++]);
        int m=n-x;
        for(int i=0;i<temp.size();i++)
            ans[temp[i]]=a[temp[(i+m/2)%m]];
        m=n-y;
        REP(i,1,n)
        {
            if(m<=0) break;
            if(!xuan[i] && a[i]==ans[i]) ans[i]=k,m--,xuan[i]=1;
        }
        REP(i,1,n)
        {
            if(m<=0) break;
            if(!xuan[i]) ans[i]=k,m--,xuan[i]=1;
        }

        int judge=0,judge2=0;
        REP(i,1,n) if(a[i]==ans[i]) judge++;
        if(judge!=x) puts("NO");
        else
        {
            puts("YES");
            REP(i,1,n) printf("%d ",ans[i]);
            puts("");
        }
    }

    return 0;
}



D

题意

思路

代码




E

题意

思路

代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值