Codeforces Round #658 (Div. 2)
A. Common Subsequence
题目描述
Example
input
5
4 5
10 8 6 4
1 2 3 4 5
1 1
3
3
1 1
3
2
5 3
1000 2 2 2 3
3 1 5
5 5
1 2 3 4 5
1 2 3 4 5
output
YES
1 4
YES
1 3
NO
YES
1 3
YES
1 2
题意思路:两个数组,分别为n个 ,m个。找一个子字符串同时属于a,b字符串。子字符串不限长度及其他条件,那只需要找到是否一个数值是两组都有的即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,a[1005],b[1005];
scanf("%d%d",&n,&m);
map<int,int>p;
for(int i=0; i<n; i++)
{
scanf("%d",&a[i]);
p[a[i]]++;
}
int flag=0,l;
for(int i=0; i<m; i++)
{
scanf("%d",&b[i]);
if(p[b[i]]!=0&&flag==0)
{
l=b[i];
flag=1;
}
}
if(flag==1)
{
printf("YES\n");
printf("1 %d\n",l);
}
else
printf("NO\n");
}
return 0;
}
B. Sequential Nim
题目描述
Example
input
7
3
2 5 4
8
1 1 1 1 1 1 1 1
6
1 2 3 4 5 6
6
1 1 2 1 2 2
1
1000000000
5
1 2 2 1 1
3
1 1 1
output
First
Second
Second
First
First
Second
First
题意思路:一个数组代表n个石堆,每个石堆数>1,两个人轮流拿正整数个,两人都发挥最佳状态,先没东西可拿的输掉,问谁会赢。通过观察给出的样例,找到规律,一开始以为与开头、结尾的1都有关。后来又写了几个又发现只与开头的一的个数有关。可以理解为先头一的话是必须只能拿走,不管是谁,这就可能会导致先手的又是消失,如果开头一的个数是奇数,则第一个不是一的石堆就是后手先拿,后手从此也就掌握了主导权,可以控制局面使得他必赢。先手也是同理,如果还是他先手(从第一个不是一的位置开始),那他也是必赢的。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
int a[100005];
scanf("%d",&n);
int num=0;
for(int i=0; i<n; i++)
{
scanf("%d",&a[i]);
if(a[i]==1)
num++;
}
if(num==n)
{
if(num%2==0)
{
printf("Second\n");
}
else
{
printf("First\n");
}
}
else
{
int l=0;
for(int i=0; i<n; i++)
{
if(a[i]!=1)
break;
else
l++;
}
if(l%2==0)
{
printf("First\n");
}
else
printf("Second\n");
}
}
return 0;
}
C1. Prefix Flip (Easy Version)
题目描述
Example
input
5
2
01
10
5
01011
11100
2
01
01
10
0110011011
1000110100
1
0
1
output
3 1 2 1
6 5 2 5 3 1 2
0
9 4 1 2 10 4 1 2 1 5
1 1
题意思路:给定a,b字符串,只能每次选择前缀和翻转并倒着放回字符串里,要求输出总共几步及每一步改变的长度(即从那个位置开始之前都翻转的)。
看了别人写的一些代码。每次只改变一个位置,即哪个位置与目标字符串不同。只改变一个位置的话,就是把这个位置之前的全部反转倒置会字符串,那那个不同的字符的位置就在第一个位置,在只改变第一个位置,最后从刚才开始边的位置再全部反转回来且倒置,这样就是除了不同的那个位置发生了变化其他都是翻着配合的。全部操作最多需要3*n次(C1)。
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
string a,b;
cin>>a>>b;
vector<int> v;
int cnt=0;
for(int i=0; i<n; i++)
{
if(a[i]!=b[i])
{
cnt++;
v.push_back(i+1);
v.push_back(1);
v.push_back(i+1);
}
}
cout<<3*cnt<<" ";
for(int i=0; i<v.size(); i++)
cout<<v[i]<<" ";
cout<<"\n";
}
}
C2. Prefix Flip (Hard Version)
题目描述
Example
input
5
2
01
10
5
01011
11100
2
01
01
10
0110011011
1000110100
1
0
1
output
3 1 2 1
6 5 2 5 3 1 2
0
9 4 1 2 10 4 1 2 1 5
1 1
题意思路:题意与C1一样只是这里约束了步骤的次数为2n次以内。
真的是别人代码一看就懂,自己就是想不到。
就是先可以将字符串全部统一成同一字符(0/1),如若这个字符与前面一个字符不同则将前面全部变成与这个字符相同,这么一趟下来走一遍最多就是n。然后再变化成目标字符串,从后面开始,若是不同,则翻转倒置(导致也没差因为这个位置之前都是一样的),这里需要注意的就是,因为反转了所以都变了,下一个位置也变了,需要一个变量记录来为下一次比较做准备。一次只考虑一个位置,让其他之前的位置都保持一致的跟着变化,依次往前走,一趟下来也是n。加一起最多就是2n次(C2)。
代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
string a,b;
cin>>a>>b;
vector<int>p;
for(int i=1; i<n; i++)
{
if(a[i]!=a[i-1])
p.push_back(i);
}
char now=a.back();
for(int i=n-1; i>=0; i--)
{
if(now!=b[i])
{
p.push_back(i+1);
now=(now=='0'?'1':'0');
}
}
printf("%d ",p.size());
for(int i=0; i<p.size(); i++)
{
printf("%d ",p[i]);
}
printf("\n");
}
return 0;
}