AtCoder Beginner Contest 465 C - Reverse Permutation 题解
貌似好像只有我是这么写的。
思路
我们发现序列中的每一个数都有一个前驱和后继,当序列颠倒时只需修改对应的数的前驱后继即可,修改规则如下:
- 从前向后遍历字符串,并设反转后的序列开头为 \(h\),开始时 \(h=1\)。
- 第 \(i\) 位为
x,不修改。 - 第 \(i\) 位为
o,将 \(i+1\) 的前驱改为 \(h\),\(h\) 的后继改为 \(h\) 的前驱。
然后你就会发现当你改完后 \(1\) 到 \(i\) 之间的数的前驱后继并没有改,它爆掉了......
不用慌,既然前驱后继改不了,那我们就不分前驱和后继了,那么我们应该改称为边。
由于一个点只有两条边,可以快速添加和删除,整个程序复杂度为 \(O(n)\)。
至于输出,由于已经知道 \(h\),每个节点只有一个出边和入边,遍历输出就行。
用图表示就是:


代码
#include<bits/stdc++.h>
using namespace std;
const int NUM=5e5+10;
int n;
string s;
int e[NUM][2];
void addedge(int x,int y){
if(!e[x][0]) e[x][0]=y;
else e[x][1]=y;
}
void deledge(int x,int y){
if(e[x][0]==y) e[x][0]=0;
else e[x][1]=0;
}
signed main(){
cin>>n>>s;s=' '+s;
for(int i=1;i<=n;++i){
addedge(i,i+1);
addedge(i,i-1);
}
int h=1;
for(int i=1;i<=n;++i){
if(s[i]=='o'){
deledge(h,0);
deledge(i,i+1);
addedge(h,i+1);
addedge(i+1,h);
h=i;
}
}
bitset<NUM> vis;
while(h&&h<=n){
cout<<h<<' ';
vis[h]=1;
if(!vis[e[h][0]]&&e[h][0]) h=e[h][0];
else h=e[h][1];
}
return 0;
}

浙公网安备 33010602011771号