CF1458D. Flip and Reverse

题目

http://codeforces.com/contest/1458/problem/D

题意

给定一个01串,定义一次操作为 选择一个01个数相同的子串,将其取反再翻转
询问经过任意次操作后这个串字典序最小是什么
\(|s|,T<=5*10^5\)

思路

将所有0看做-1 1看做-1
计算它们的前缀和
对一个区间取反再翻转 就相当于把这个前缀和区间翻转
按照原字符串建一张图,对于每个x建一个点
设当前x值为k 遇到一个1 就从k到k+1连一条无向边
当一个子串可以操作时,相当于在一个环上走一圈
然后贪心能往小的数走就往小的数走
从k走到k-1当且仅当没有k+1或者k和k-1之间有至少2条边(还有1条边返回k)
\(O(\Sigma(n))\)

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
int a[maxn],lst[maxn];
char c[maxn];
int main(){
//    ios::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%s",c);
		int n=strlen(c);
		for(int i=0;i<n;i++)
		{
			a[i]=c[i]-'0';
			if(a[i]==0)
				a[i]=-1;
		}
		int sum=0;
		for(int i=0;i<n;i++)
		{
			lst[max(sum,sum+a[i])+n]++;
			sum+=a[i];
		}
		int j=n;
		for(int i=0;i<n;i++)
		{
			if(lst[j]>=2)
			{
				putchar('0');
				lst[j]--;
				j--;
			}
			else if(lst[j+1]>=2)
			{
				putchar('1');
				j++;
				lst[j]--;
			}
			else if(lst[j])
			{
				putchar('0');
				lst[j]--;
				j--;
			}
			else if(lst[j+1])
			{
				putchar('1');
				j++;
				lst[j]--;
			}	
			else
				return 0;
		}
		puts("");
		for(int i=0;i<=2*n;i++)
			lst[i]=0;
	}
	return 0;
}

提示

莫名其妙tle 换个快读输出换成puts、string换成char 就好了。。。

参考:https://blog.csdn.net/RA100FDM/article/details/113838183
https://www.cnblogs.com/zkyJuruo/p/14163445.html

posted @ 2021-02-24 21:37  mushroom_spy  阅读(166)  评论(0)    收藏  举报