A1 | A2 | B | C | D | E |
---|---|---|---|---|---|
√ | ○ | √ | ○ |
( √:做出; ●:尝试未做出; ○:已补题 )
题目地址: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 n≤1000),操作次数不超过 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 n≤1e5 ,然后操作次数不超过 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 4
,7
, 6 1 5
,3 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 2
和 b: 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
题意:
思路:
代码: