【解题报告】CF DIV2 #ROUND 707 A~D
比赛链接
字符串专场hh
5059名,失误挺多的,不过还好连跪三把之后没有继续掉分,回升了一点
A.Déjà Vu
思路
这次A题没有一发瞬秒而且严重拖慢了进度,原本憨憨交了一发TLE。
思路还是很简单的,如果全是a那就输出NO,反之可以插入得到非回文串,接下来让我们瞅瞅原本TLE的代码
TLE代码
// Problem: A. Déjà Vu
// Contest: Codeforces - Codeforces Round #712 (Div. 2)
// URL: https://codeforces.com/contest/1504/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
/*DATA & KEY
t 1 1e4
s len 3*1e5
*/
int T;
void solve(int T)
{
//NEW DATA CLEAN
map<char,int>mp;
mp.clear();
//NOTE!!!
string s;
cin>>s;
int len=s.size();
for(int i=0;s[i];i++)mp[s[i]]++;
if(mp.size()==1&&mp['a']>0)puts("NO");
else
{
string in="a";
string A=in+s;
string tmp_A=A;
reverse(tmp_A.begin(),tmp_A.end());
if(tmp_A!=A)
{
puts("YES");
cout<<A<<endl;
}
else
{
for(int i=0;s[i];i++)
{
string a=s.substr(0,i)+in+s.substr(i);
string tmp=a;
reverse(tmp.begin(),tmp.end());
if(a!=tmp)
{
puts("YES");
cout<<a<<endl;
break;
}
}
}
}
}
int main()
{
scanf("%d",&T);
while(T--)solve(T);
return 0;
}
显然的,我们对STL的reverse函数复杂度太不熟悉了,reverse操作时间复杂度为
O
(
N
)
O(N)
O(N),我以为是
O
(
1
)
O(1)
O(1)然后瞬间TLE啊啊啊。
如果构造类似这种右边a比左边多1个的,就要循环好几遍
aaaaaabaaaaaaa
然后改进了一下思路,其实要插成非回文串只要看看在开头插入和在末尾插入即可,哪个ok输出哪个
AC代码
// Problem: A. Déjà Vu
// Contest: Codeforces - Codeforces Round #712 (Div. 2)
// URL: https://codeforces.com/contest/1504/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
/*DATA & KEY
t 1 1e4
s len 3*1e5
*/
int T;
void solve(int T)
{
//NEW DATA CLEAN
//NOTE!!!
bool flag=1;
string s;
cin>>s;
int len=s.size();
for(int i=0;s[i]&&flag;i++)
{
if(s[i]!='a')flag=0;
}
if(flag)puts("NO");
else
{
puts("YES");
string A="a"+s;
string B=s+"a";
string a,b;
a=A,b=B;
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
if(a!=A)cout<<A<<endl;
else cout<<B<<endl;
}
}
int main()
{
scanf("%d",&T);
while(T--)solve(T);
return 0;
}
B. Flip the Bits
思路
花了20分钟一发过了,爽唉。
先遍历一遍,把所有允许的前缀操作记录一波,我用了map,其实bool数组就ok了数据范围挺小的
题目要求字符串a通过前缀处理变成字符串b,前缀处理的话要考虑后面的覆盖问题。所以我们反过来由字符串b变成字符串a,这样的话,保证最新一次操作是不会被之前操作所影响的。
代码
// Problem: B. Flip the Bits
// Contest: Codeforces - Codeforces Round #712 (Div. 2)
// URL: https://codeforces.com/contest/1504/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
/*DATA & KEY
T 1 1E4
sum n 1 3e5
*/
int T;
void solve(int T)
{
//NEW DATA CLEAN
//NOTE!!!
int n;cin>>n;
string a,b;cin>>a>>b;
map<int,int>mp;
int c1=0,c0=0;
for(int i=0;i<n;i++)
{
if(a[i]=='0')c0++;
else c1++;
if(c0==c1)mp[i]++;
}
bool flag=1,ans=1;
for(int i=n-1;i>=0;i--)
{
bool aa=a[i]-'0';
bool bb=b[i]-'0';
bb=flag?bb:(!bb);//标记反转
if(aa!=bb)//从一但不同就看能不能翻转
{
if(mp.find(i)!=mp.end())flag=!flag;
else {ans=0;break;}
}
}
if(ans)puts("YES");
else puts("NO");
}
int main()
{
scanf("%d",&T);
while(T--)solve(T);
return 0;
}
C. Balance the Bits
思路
构造题
平衡括号串就是合法括号配对嘛,然后首先想到了利用栈来判断括号配对是否合法,然后瞎搞了一波直接白给,后面想了个贪心构造的结果写了一堆后忘记是要输出构造结果的,还有五分钟直接放弃
参考了这个大佬的博客
《很 了 解 平 衡 括 号 串》
新知识get!
然后可以确定的是开头和结尾一定是(和)组成的,因此字符串首尾必是1
假设原来全都是1,所以如果有一个变为0,那么肯定会产生一个不匹配的括号,因此需要再来一个0来抵消这次不匹配,所以0的数量一定是偶数
那么如何构造呢?比赛的时候构造有问题,后来参看了这个大佬的博客
根据特性2:任意前缀的)的数目不能大于(
所以我们构造的时候要保证两个串在当前位置(数量比)多,对于最后一个位置我们已经确定了一定是),所以不用特别处理
()一样大,前缀(一直比)多,用0来抵消0 基于这三点我们进行如下构造
前一半1为(,后一半为)
奇数次0,a加(,b加)
偶数次0,a加),b加(
代码
// Problem: C. Balance the Bits
// Contest: Codeforces - Codeforces Round #712 (Div. 2)
// URL: https://codeforces.com/contest/1504/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
/*DATA & KEY
t 1 1e4
sum n 2 2e5
n even
*/
int T;
void solve(int T)
{
//NEW DATA CLEAN
//NOTE!!!
int n;cin>>n;
string s;cin>>s;
int cnt=0;
for(int i=0;s[i];i++)
if(s[i]=='0')cnt++;
int mid=(n-cnt)/2;//标记前一半1的数量
if((cnt&1)||s[0]=='0'||s[n-1]=='0')puts("NO");
else
{
string a,b;
int ca=0,cb=0;
bool flag=1;
puts("YES");
for(int i=0;i<n;i++)
{
if(s[i]=='1')
{
ca++;
if(ca<=mid)a+='(',b+='(';
else a+=')',b+=')';
}
else
{
if(flag)a+='(',b+=')';
else a+=')',b+='(';
flag=!flag;
}
}
cout<<a<<endl<<b<<endl;
}
}
int main()
{
scanf("%d",&T);
while(T--)solve(T);
return 0;
}
D. 3-Coloring
思路
交互题,蛮新奇的以前没碰到过。参考了繁凡巨佬的博客才知道要干嘛。
染色问题,总共可以染三种颜色,相邻格子颜色不能相同。每次会告诉你一种颜色不能染的,问你如何把nxn的棋盘染满。
不相邻染色可以想到黑白二分图染色,不过这个有三个颜色咋整嘞,可以转成两次不相邻二染色。
先按照繁凡巨佬说的预处理棋盘,可以发现黑白可以分为i+j为偶数和奇数的两种。
第一次二染色:颜色1全填黑,另外两种全填白
第二次二染色:黑的或者白的一种填完了,那就随便填,因为相邻的已经被黑或者白块全部分隔开了
代码
借(完)鉴(全)了(照)一(搬)波
// Problem: D. 3-Coloring
// Contest: Codeforces - Codeforces Round #712 (Div. 2)
// URL: https://codeforces.com/contest/1504/problem/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// FishingRod
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
/*DATA & KEY
*/
int T;
int n,m;
const int N=3;
vector<PII>pos[N];
void solve()
{
//NEW DATA CLEAN
//NOTE!!!
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)//棋盘预处理
for(int j=1;j<=n;j++)
pos[((i+j)&1)+1].push_back(make_pair(i,j));//写得好精简啊
for(int i=1;i<=n*n;i++)
{
int ban,col;//ban表示禁掉的,col表示选的颜色
PII position;
scanf("%d",&ban);
if(ban==1)
{
if(pos[2].size())//如果还没被填满
{
col=2;
position=pos[2].back();
pos[2].pop_back();
}
else
{
col=3;
position=pos[1].back();
pos[1].pop_back();
}
}
else if(ban==2)
{
if(pos[1].size())
{
col=1;
position=pos[1].back();
pos[1].pop_back();
}
else
{
col=3;
position=pos[2].back();
pos[2].pop_back();
}
}
if(ban==3)
{
if(pos[1].size())
{
col=1;
position=pos[1].back();
pos[1].pop_back();
}
else if(pos[2].size())
{
col=2;
position=pos[2].back();
pos[2].pop_back();
}
}
printf("%d %d %d\n",col,position.first,position.second);
fflush(stdout);//交互题一定要加这玩意,而且别用endl因为自己换成\n了没刷新
}
}
int main()
{
// scanf("%d",&T);
// while(T--)solve(T);
solve();
return 0;
}
反思
A:
STL reverse:
时间复杂度为
O
(
N
)
O(N)
O(N)
错误的写法
tmp=reverse(tmp_A.begin(),tmp_A.end());
reverse(tmp);
正确的写法
reverse(tmp_A.begin(),tmp_A.end());
B:
前缀操作涉及覆盖的可以逆向思考,反向操作,从后向前遍历,保证本次操作后半部分的效果不会被新操作的覆盖。
C:
平衡括号串(合法括号串)特性与联想:
- (和)各占一半
- 开头和结尾分别为(和)
- 对于任意前缀(一定比)数量多
- 联想到用栈来判断是否合法
其中1,3为平衡括号串的充要条件
对于这题里的匹配的联想:如果出现某个操作使得原本匹配的变成不匹配了,就思考如何抵消本次操作造成的影响来变成合法的。可能是再进行一次这种操作,也可能是选择别的操作。另外很多时候可以把两个不匹配的配对变成匹配的,原本匹配的就和原本匹配的配对
D:
不相邻染色联想到二分图二染色问题,多种颜色可以分为多次而染色。先预处理出黑白棋盘,加入黑白染色队列。