Codeforces Round #658 (Div. 2) 题解

废话

赛时写了前 5 5 5 题,拿了个五十几的排名当时沾沾自喜,然后重测时 T3,T4 数组开小同时爆炸了……

T1

只需要找到 a , b a,b a,b 数组的一个相同元素即可。

(大概只有我这种菜逼才会在这题WA一发吧……)

代码如下:

#include <cstdio>
#include <cstring>
 
int T,n,m;
bool v[1010];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d",&n,&m);
		memset(v,false,sizeof(v));
		for(int i=1,x;i<=n;i++)scanf("%d",&x),v[x]=true;
		int ans=-1;
		for(int i=1,x;i<=m;i++){
    
    
			scanf("%d",&x);
			if(v[x])ans=x;
		}
		if(ans!=-1)printf("Yes\n1 %d\n",ans);
		else printf("No\n");
	}
}

T2

可以发现,答案其实只取决于开头有多少个 1 1 1

假设去掉这段 1 1 1,对于一个不为 0 0 0 的数 a i a_i ai,此时的先手可以有两种操作:1、取 a i − 1 a_i-1 ai1 个;2、取 a i a_i ai 个,通过这两种操作,可以完全控制后手的操作。

控制后手操作分两种情况:

a i + 1 > 1 a_{i+1}>1 ai+1>1 时,先手取 a i − 1 a_i-1 ai1 个,后手只能取 1 1 1 个,那么 a i + 1 a_{i+1} ai+1 的操作权依然在先手手里。

假如 a i a_i ai 的后面是一段 1 1 1,那么先手可以通过上面两种操作决定这段 1 1 1 的最后一个 1 1 1 的操作权在谁手里,或者说,是这段 1 1 1 后面的那个 > 1 >1 >1 a j a_j aj 的操作权在谁手里。

所以代码如下:

#include <cstdio>
 
int T,n,a[1000010];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		int ans=1;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		for(int i=1;i<n;i++)if(a[i]==1)ans^=1;else break;
		if(ans)printf("First\n");
		else printf("Second\n");
	}
}

T3

因为每次一定要翻转一段前缀,容易想到从后往前构造,即从后往前让 a a a b b b 逐渐相同。

假设此时操作到第 i i i 位, i + 1 i+1 i+1 ~ n n n 位都已经相等了。

假如 a i = b i a_i=b_i ai=bi,那么无事发生,跳过。

假如 a i ≠ b i a_i\neq b_i ai=bi,那么肯定要翻转一次 1 1 1 ~ i i i 这个前缀,翻转后 a 1 a_1 a1 取代 a i a_i ai

那么此时看一看, a 1 a_1 a1 取反之后是否等于 b i b_i bi,假如不是,那么就先翻转 1 1 1 ~ 1 1 1,这样可以将 a 1 a_1 a1 取反,然后再翻转 1 1 1 ~ i i i 即可。

时间复杂度 O ( n 2 ) O(n^2) O(n2),最大操作次数不超过 3 n 3n 3n,可以通过。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 2010
 
int T,n,ans[maxn],t;
char S1[maxn],S2[maxn];
void sel(int x)
{
    
    
	ans[++t]=x;
	for(int i=1;i<=x;i++)
	if(S1[i]=='1')S1[i]='0';else S1[i]='1';
	for(int i=1;i<=x/2;i++)swap(S1[i],S1[x-i+1]);
}
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);t=0;
		scanf("%s %s",S1+1,S2+1);
		for(int i=n;i>=1;i--)
		if(S1[i]==S2[i])continue;
		else{
    
    
			if(S1[1]==S2[i])sel(1);
			sel(i);
		}
		printf("%d ",t);for(int i=1;i<=t;i++)printf("%d ",ans[i]);printf("\n");
	}
}

T4

当时发现上面那个构造方法的操作次数也恰好并没有超过 2 n 2n 2n,于是就想着直接拿来用。

发现翻转这个过程可以优化,可以用一个链表来维护前缀,那么翻转就相当于交换首尾指针,这样可以做到 O ( n ) O(n) O(n)

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 200010
 
int T,n,ans[maxn],t;
char S1[maxn],S2[maxn];
struct node{
    
    int x;node *next,*last;}*s[maxn];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);t=0;
		scanf("%s %s",S1+1,S2+1);
		for(int i=1;i<=n;i++)s[i]=new node();
		s[0]=s[n+1]=NULL;
		for(int i=1;i<=n;i++)s[i]->x=S1[i]-'0',s[i]->next=s[i+1],s[i]->last=s[i-1];
		node *st=s[1],*ed=s[n];int now=0;
		for(int i=n;i>=1;i--)
		if((ed->x^now)==S2[i]-'0')
		{
    
    
			if(i==1)continue;
			if(!ed->next)ed=ed->last,ed->next=NULL;
			else ed=ed->next,ed->last=NULL;
		}
		else
		{
    
    
			if((st->x^now)==S2[i]-'0')ans[++t]=1,st->x^=1;
			ans[++t]=i;swap(st,ed);now^=1;
			if(i==1)continue;
			if(!ed->next)ed=ed->last,ed->next=NULL;
			else ed=ed->next,ed->last=NULL;
		}
		printf("%d ",t);for(int i=1;i<=t;i++)printf("%d ",ans[i]);printf("\n");
	}
}

T5

容易发现一个性质,对于一个 i , j i,j i,j,假如满足 p i < p j p_i<p_j pi<pj,且 ∀ k ∈ ( i , j ) , p k < p i \forall k\in(i,j),p_k<p_i k(i,j),pk<pi,那么 i i i j − 1 j-1 j1 这一段肯定属于同一个原数组。

那么我们就可以将 p p p 分成若干段,看看能不能将这些段分成两堆,每堆大小为 n n n 即可,可以做一下背包。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 2010
 
int T,n,s[maxn],t,f[maxn];
 
int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d",&n);
		int last;t=0;
		for(int i=1,x,tot=0;i<=(n<<1);i++){
    
    
			scanf("%d",&x);
			if(i==1)last=x,tot++;
			else{
    
    
				if(last>x)tot++;
				else s[++t]=tot,last=x,tot=1;
			}
		}
		memset(f,0,sizeof(f));
		f[0]=1;
		for(int i=1;i<=t;i++)
		for(int j=n;j>=s[i];j--)
		f[j]|=f[j-s[i]];
		if(f[n])printf("YES\n");
		else printf("NO\n");
	}
}

T6

当时想出了大概的思路,但是由于当时思路不清晰加上各种菜,所以当时并没有写出来,并且后来发现自己的思路有一个小地方是锅了的,所以这里讲讲题解做法。

首先设 o t h oth oth 为整个序列中没有出现过的颜色,显然存在这样的一个颜色。

假设已经选定了哪 x x x 个位置不动,设 i d x idx idx 表示剩下 n − x n-x nx 个位置中出现次数最多的颜色的出现次数,那么无论如何交换颜色,至少会有 2 × i d x − ( n − x ) 2\times idx-(n-x) 2×idx(nx) 个位置依然保持原来的颜色。

但是我们要求剩下的 n − x n-x nx 个位置不能有和原来相同的颜色,所以要将那些相同的位置替换为 o t h oth oth,我们需要进行 n − y n-y ny 次,假如换完还有相同就无解了,即 2 × i d x − ( n − x ) > n − y 2\times idx-(n-x)>n-y 2×idx(nx)>ny 则无解。

有解的话,将剩下的 n − x n-x nx 个颜色排个序,然后将颜色整体左移 ⌊ n − x 2 ⌋ \lfloor \dfrac {n-x} 2 \rfloor 2nx 位即可,相同的强制换成 o t h oth oth,这样就做完了。

我之前的做法和题解不一样的就是最后的答案构造,我的做法很蠢,我是保留出现次数最少的 y − x y-x yx 个颜色,然后在后面放 n − y n-y ny o t h oth oth,然后整体左移,这样的问题在于, o t h oth oth 的位置不一定是连续的,可能在那 y − x y-x yx 个的中间。

代码如下:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100010

int T,n,x,y,a[maxn];
vector<int>col[maxn],tot[maxn],s;
int ans[maxn];
bool v[maxn];

int main()
{
    
    
	scanf("%d",&T);while(T--)
	{
    
    
		scanf("%d %d %d",&n,&x,&y);
		for(int i=0;i<=n+1;i++)col[i].clear(),tot[i].clear(),ans[i]=0,v[i]=false;
		for(int i=1;i<=n;i++)scanf("%d",&a[i]),col[a[i]].push_back(i);
		for(int i=1;i<=n+1;i++)tot[col[i].size()].push_back(i);
		int idx=n;for(int i=1;i<=x;i++){
    
    
			while(!tot[idx].size())idx--;
			int z=tot[idx].back();
			ans[col[z].back()]=z;
			col[z].pop_back();
			tot[idx].pop_back();
			tot[idx-1].push_back(z);
		}
		while(!tot[idx].size())idx--;
		if(idx*2-(n-x)>n-y){
    
    printf("NO\n");continue;}
		s.clear();for(int i=1;i<=idx;i++)
		for(int j=0;j<tot[i].size();j++)s.insert(s.end(),col[tot[i][j]].begin(),col[tot[i][j]].end());
		int ch_=n-y;
		#define ch(x) ans[x]=tot[0][0],ch_--,v[x]=true
		for(int i=0;i<n-x;i++){
    
    
			ans[s[i]]=a[s[(i+(n-x)/2)%(n-x)]];
			if(ans[s[i]]==a[s[i]])ch(s[i]);
		}
		for(int i=0;ch_;i++)if(!v[s[i]])ch(s[i]);
		printf("YES\n");
		for(int i=1;i<=n;i++)printf("%d ",ans[i]);
		printf("\n");
	}
}

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/107530864