其他分享
首页 > 其他分享> > CF1458D Flip and Reverse

CF1458D Flip and Reverse

作者:互联网

一、题目

点此看题

二、解法

没有什么好的想法,就从图论的角度入手吧。

要根据题目特性来建图,首先要考虑把什么当做点的问题,如果把字符串的元素当成点是不好表示 子串必须包含同样数量的字符0与1 这个限制的。但是前缀和可以方便地表示这个限制,令 \(1\) 为 \(1\),\(0\) 为 \(-1\),那么如果 \(sum_l=sum_r\) 就说明这是一个可以操作的区间,那么我们把前缀和建成点

还要考虑怎么表示字符串里的元素,直接当成边建上去,对于元素 \(s_i\),将点 \(sum_{i-1}\) 和 \(sum_i\) 连一条标记为 \(s_i\) 的边,考虑原始字符串就对应了这张图里的某一条欧拉回路(因为要把所有的边访问完)

那么把转换和翻转操作对应到图上,因为有边相连的两个点编号相差 \(1\),所以这张图是很特别的。首先选出一条起点终点相同的路径,然后把沿路经过的边换方向即可,你会发现新的路径正好对应操作之后的字符串。

现在的问题是最小化字典序,有一个结论:所有欧拉路径都对应着操作后的合法字符串。考虑走到 \(x\) 之后出现了两种存在欧拉路径的选择 \(x-1\) 和 \(x+1\),因为都是欧拉路径所以往一边走一定能走回来,那么说明存在边 x-1->xx+1->x,如果应该走 x+1(即是对应原字符串的走法)那么是 x->x+1->x->x-1->x,可以通过操作换成 x->x-1->x->x+1->x,这正好对应走 x-1 的方法,虽然它并不对应原字符串但是合法的。

那么找到原图定起点定终点,经过标记字典序最小的欧拉回路即可,根据图的特性可以设计如下贪心。

代码非常好写,时间复杂度 \(O(n)\)

三、总结

套路:差分,前缀和。差分可以在区间打标记修改之类的问题使用,前缀和在有区间权值限制的问题使用。

如果要用图论,思考每类元素的意义(是建成边还是建成点),学会把限制建在图上是很重要的。

#include <cstdio>
#include <cstring>
const int M = 500005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,a[2*M][2];char s[M];
void work()
{
    scanf("%s",s+1),n=strlen(s+1);
    int x=n;
    for(int i=1;i<=n;i++)
    {
        a[x][s[i]-'0']++;
        if(s[i]=='0') x--;
        else x++;
    }
    x=n;
    for(int i=1;i<=n;i++)
    {
        if(a[x][0] && a[x-1][1]) a[x][0]--,x--,printf("0");
        else if(a[x][1]) a[x][1]--,x++,printf("1");
        else a[x][0]--,x--,printf("0");
    }
    puts("");
}
signed main()
{
    T=read();
    while(T--) work();
}

标签:Reverse,sum,路径,Flip,CF1458D,字符串,对应,欧拉,前缀
来源: https://www.cnblogs.com/C202044zxy/p/14967601.html