【学习笔记】CF1458D Flip and Reverse

其实是因为写不动带花树所以来水博客

这题妙啊。在学长的精心讲解下大概搞懂了。

首先这个操作就很神奇。考虑将 1 1 1看成 + 1 +1 +1 0 0 0看成 − 1 -1 1,求出其前缀和数组,然后每次就是两个前缀和相同的位置进行翻转,更神奇的是因为位置和值同时翻转了,所以你发现相当于是把前缀和数组的区间 [ l , r ] [l,r] [l,r]拿来翻转了。

于是就有一些更神奇的操作了。考虑构造无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V = { s u m i } V=\{sum_i\} V={sumi} E = { ( s u m i − 1 , s u m i ) ∣ 0 ≤ i < n } E=\{(sum_{i-1},sum_i)|0\le i<n\} E={(sumi1,sumi)∣0i<n},发现无论怎么操作这个序列对应的 G G G都是不变的,并且一个序列恰好经过所有边一次所以对应一个欧拉路径,那么大胆猜测一个欧拉路径和序列构成双射关系(?)。或者也可以换一个角度来看,相当于一个环可以顺时针或者逆时针走。并且这个图非常神奇, i i i只可能连到 i − 1 i-1 i1或者 i + 1 i+1 i+1,所以据此贪心的求出字典序最小的欧拉路径是容易的。

复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=5e5+5;
int T,n,Min,sum[N],edges[N][2];
string s;
void dfs(int x){
    if(edges[x][0])assert(x);
    if(!edges[x][0]&&!edges[x][1])return;
    if(edges[x][0]&&edges[x-1][1]>1||!edges[x][1]){
        cout<<0;edges[x][0]--,edges[x-1][1]--,dfs(x-1);
    }
    else {
        cout<<1;edges[x][1]--,edges[x+1][0]--,dfs(x+1);
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        cin>>s,n=s.size();Min=0;sum[0]=0;
        for(int i=1;i<=n;i++){
            sum[i]=sum[i-1]+(s[i-1]=='1'?1:-1);
            Min=min(Min,sum[i]);
        }for(int i=0;i<=n;i++){
            sum[i]-=Min;
        }for(int i=0;i<=n;i++)edges[i][0]=edges[i][1]=0;
        for(int i=0;i<n;i++){
            if(s[i]=='1'){
                edges[sum[i]][1]++;
                edges[sum[i+1]][0]++;
            }
            else{
                edges[sum[i]][0]++;
                edges[sum[i+1]][1]++;
            }
        }
        dfs(sum[0]);cout<<"\n";
    }
}
posted @ 2023-07-03 17:00  仰望星空的蚂蚁  阅读(11)  评论(0)    收藏  举报  来源