题解:「ROI 2017 Day 2」存储器
题目信息
题目链接
题目描述
给定一个字符串 \(S\),设其长度为 \(n\),每个字符要么是 + 要么是 -。
定义一个片段为 \(S\) 的一个子串 \(S[l,r]\) 满足下面三个条件:
- \(l=1\) 或者 \(S_{l-1} \ne S_l\)。
- \(r=n\) 或者 \(S_{r+1} \ne S_r\)。
- \(S_l=S_{l+1}=\dots=S_{r-1}=S_r\)。
定义一次变换为:
- 选择 \(S\) 的两个相邻且长度不同的片段,改变长度较小的那个片段的所有字符为其相反字符。(
+变为-,-变为+)
现在有 \(q\) 次询问,每次询问给出只包含 + 和 - 并且长度相同的字符串 \(S_i,T_i\),请你判断 \(S_i\) 是否能够通过若干次变换得到 \(T_i\)。
输入格式
第一行一个整数 \(q\) 表示询问次数。
接下来 \(q\) 行每行两个字符串 \(S_i,T_i\),含义如题所示。
输出格式
对于每次询问输出一行一个字符串 Yes 或 No 表示是否可以完成题目所给要求。
样例输入 #1
3
++- +++
++-- ++++
++-+--+- ++++++++
样例输出 #1
Yes
No
Yes
样例输入 #2
3
++-+-- ++----
++-+-- +++---
-++- -++-
样例输出 #2
Yes
No
Yes
数据范围
注:本题只放部分数据,完整数据请左转 LOJ P2770 评测。
设 \(len=\sum |S_i|\)。
| 子任务编号 | 分值 | $1 \le len \le $ | 特殊性质 |
|---|---|---|---|
| \(1\) | \(20\) | \(16\) | \(T_i\) 中没有 - |
| \(2\) | \(30\) | \(10^3\) | \(T_i\) 中没有 - |
| \(3\) | \(20\) | \(10^6\) | \(T_i\) 中没有 - |
| \(4\) | \(20\) | \(10^3\) | 无 |
| \(5\) | \(10\) | \(10^6\) | 无 |
思路
很简单。
引理
将 \(S\) 分为一些片段后,每一个片段在 \(T\) 中都应该是相同的字符。例如在 \(S\) 中这个片段为
-----,则在 \(T\) 中应该对应+++++或-----。
这个证明过程很简单。因为每一次修改都是将片段修改成片段,所以不存在片段不对应的情况。
做法
由上面的引理,可以知道其中一种判断 -1 的情况。
接着我们就知道需要被翻转的片段是哪些。对于一个片段,如果它能够被翻转,我们就会把这个片段翻转掉。假设从左往右中片段的长度分别为 \(llen,len,rlen\)。如果 \(llen+rlen>len\),那么这个片段一定会被合并变成 \(llen+len+rlen\)。此时与原序列的变化就是把左边和右边的变长了,于是就可能更新其它的需要被翻转的片段。如果到了最后仍然不能确定,那么就无解。
Code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
// Fast IO
void read(int &x){
x = 0;int h = 1;char tmp;
do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
x*=h;
}
void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
void write(char x){putchar(x);}
void write(int x){
if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
do{st[++tot]=x%10,x/=10;} while(x);
while(tot){putchar(st[tot--]+'0');};
}
void write(int x,char y){write(x);write(y);}
const int MAXN = 1e6+10;
int n,tot;
char a[MAXN],b[MAXN];
struct block{
int l,r,len,llen,rlen;
bool use;
}p[MAXN];
int lst[MAXN],nxt[MAXN];
bitset<MAXN> inque;
queue<int> q;
signed main(){
scanf("%s%s",a+1,b+1);
n = strlen(a+1);tot = 0;
for(int i = 1;i<=n;i++){
p[++tot].l = i;
while(i<=n&&a[i]==a[p[tot].l]) i++;
i--;
p[tot].r = i;
p[tot].len = p[tot].r-p[tot].l+1;
p[tot].use = 0;
}
inque.reset();
while(!q.empty()) q.pop();
int sum = 0;
p[tot+1].l = n;
p[tot+1].r = n;
p[tot+1].use = 0;
nxt[tot+1] = tot+1;
p[tot+1].len = 0;
p[tot+1].llen = 0;
p[tot+1].rlen = 0;
p[0].l = 1;
p[0].r = 1;
p[0].use = 0;
lst[0] = 0;
p[0].len = 0;
p[0].llen = 0;
p[0].rlen = 0;
for(int i = 1;i<=tot;i++) lst[i] = i-1;
for(int i = 1;i<=tot;i++) nxt[i] = i+1;
bool is_OK = 1;
int psum = 0;
for(int i = 1;i<=tot;i++){
p[i].llen = p[i-1].len;
p[i].rlen = p[i+1].len;
for(int j = p[i].l;j<=p[i].r;j++) is_OK &= (b[j]==b[p[i].l]);
if(a[p[i].l]!=b[p[i].l]) p[i].use = 1,psum++;
}
if(!is_OK) return puts("No"),0;
for(int i = 1;i<=tot;i++){
if(p[i].use&&max(p[i].llen,p[i].rlen)>p[i].len){
q.push(i);
sum++;
inque.set(i);
}
}
while(!q.empty()){
int k = q.front();q.pop();
p[lst[k]].r = p[nxt[k]].r;
p[lst[k]].len = p[lst[k]].r-p[lst[k]].l+1;
lst[nxt[nxt[k]]] = lst[k];
nxt[lst[k]] = nxt[nxt[k]];
p[lst[lst[k]]].rlen = p[lst[k]].len;
p[nxt[nxt[k]]].llen = p[lst[k]].len;
if(p[lst[lst[k]]].use&&max(p[lst[lst[k]]].llen,p[lst[lst[k]]].rlen)>p[lst[lst[k]]].len){
if(!inque[lst[lst[k]]]){
q.push(lst[lst[k]]);
sum++;
inque.set(lst[lst[k]]);
}
}
if(p[nxt[nxt[k]]].use&&max(p[nxt[nxt[k]]].llen,p[nxt[nxt[k]]].rlen)>p[nxt[nxt[k]]].len){
if(!inque[nxt[nxt[k]]]){
q.push(nxt[nxt[k]]);
sum++;
inque.set(nxt[nxt[k]]);
}
}
if(p[lst[k]].use||p[nxt[k]].use) return puts("No");
}
puts(sum==psum?"Yes":"No");
return 0;
}
}
signed main(){
// freopen("store.in","r",stdin);
// freopen("store.out","w",stdout);
// ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T = 1;
gtx::read(T);
while(T--) gtx::main();
return 0;
}
tag
ROI
链表、队列、思维

浙公网安备 33010602011771号