分类讨论的做法,贪心的进行一个放一个的操作可以大大减少需要分类讨论的情况,但是有两种情况需要特别注意:

即两个0连在一起替换的情况,还有三个子串连成一个这种的情况需要特别的进行讨论.

这两种情况,贪心的做法很有可能出现错误,比如说三个子串连成一个这种,假如用贪心的做法,放一个最优再放一个当前最优,比如这样的数据:

22

1110111000001110110111

会导致出错,因为贪心的选择不会将那三个子串连成一个子串,导致不是最优解。

下面贴上代码

大家有什么建议或者问题可以在评论区回复我

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
char a[1000010];
int b[1000010];
int he(int x)
{
	int fl=(x*(x+1))/2;//求和函数,比较方便 
	return fl;
}
void solve()
{
	int n;cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	int sum=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='0') sum++;
	}
	if(sum<=2)
	{
		cout<<he(n)<<endl;//特判的情况 
		return;
	}
	sum=0;
	int now=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='1')
		{
			now++;
		}
		else
		{
			b[i-1]=now;
			now=0;
		}
	}
	if(now!=0)
	{
		b[n]=now;
		now=0;
	}
	//上面的是每个1子串末尾位置的代表的个数 
	for(int i=1;i<=n;i++)
	{
		if(b[i]!=0)
		{
			sum=sum+he(b[i]);
		}
	}
	//这里把初始的和求一遍 
	now=0;
	for(int i=n;i>=1;i--)
	{
		if(a[i]=='1')
		{
			now++;
		}
		else
		{
			b[i+1]=now;
			now=0;
		}
	}
	if(now!=0)
	{
		b[1]=now;
		now=0;
	}
	//这里是倒着遍历,把每个1子串最开始的地方标注上这个1子串的长度 
	int ans=0;//最后的答案
//	********************************************** 
	int fl1=0;//这里是求只替换一个0时候的情况 
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='0')
		{
			int duo=he(b[i-1])+he(b[i+1]);//不把0变成1时候的贡献 
			int pan=he(b[i-1]+1+b[i+1]);//把0变成1时候的贡献 
			fl1=max(fl1,(pan-duo));
		}
	}
//	********************************************** 
	int fl2=0;//这里是求两个0连起来的情况 
	for(int i=1;i<=n-1;i++)
	{
		if(a[i]=='0'&&a[i+1]=='0')
		{
			int duo=he(b[i-1])+he(b[i+2]);
			int pan=he(b[i-1]+2+b[i+2]);
			fl2=max(fl2,(pan-duo));
		}
	}
//	**********************************************
	int fl3=0;//这里是求将三个子串连成一个情况 
	int sig1=0;//唉,赛时就是这种情况没写上,遗憾银首 
	int sig1z;
	int sig1y;
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='0')
		{
			if(sig1==1)
			{
				int pan=he(sig1z+1+sig1y+1+b[i+1]);
				int duo=he(sig1z)+he(sig1y)+he(b[i+1]);
				fl3=max(fl3,(pan-duo));
				sig1=1;
				sig1z=(b[i-1]);
				sig1y=(b[i+1]);
			}
			else
			{
				sig1=1;
				sig1z=(b[i-1]);
				sig1y=(b[i+1]);
			}
		}
	}
//	**********************************************
	int dai=0;//再这种情况下总共的答案 
	int fl4=0;//这里是进行贪心求解,先放一个最优的进去,再将这个0换成1再进行一次预处理,再放一个最优的进去 
	int xb=0;//下标 
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='0')
		{
			int duo=he(b[i-1])+he(b[i+1]);
			int pan=he(b[i-1]+1+b[i+1]);
			if((pan-duo)>fl4)
			{
				fl4=(pan-duo);
				xb=i;//得到下标 
			}
		}
	}
	a[xb]='1';//将其更改为1 
	for(int i=1;i<=n;i++) b[i]=0;//将前后缀数组清空 
	//再进行一次前后缀处理 
	now=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='1')
		{
			now++;
		}
		else
		{
			b[i-1]=now;
			now=0;
		}
	}
	if(now!=0)
	{
		b[n]=now;
		now=0;
	}
	for(int i=1;i<=n;i++)
	{
		if(b[i]!=0)
		{
			dai=dai+he(b[i]);
		}
	}
	now=0;
	for(int i=n;i>=1;i--)
	{
		if(a[i]=='1')
		{
			now++;
		}
		else
		{
			b[i+1]=now;
			now=0;
		}
	}
	if(now!=0)
	{
		b[1]=now;
		now=0;
	}
	//再进行一次前后缀处理 
	int xwc=0;//这里再将第二次放的最优贡献记录下来 
	for(int i=1;i<=n;i++)
	{
		if(a[i]=='0')
		{
			int duo=he(b[i-1])+he(b[i+1]);
			int pan=he(b[i-1]+1+b[i+1]);
			xwc=max(xwc,(pan-duo));
		}
	}
	dai=dai+xwc; 
	fl4=dai-sum;//将新得到的和减去之前的和,得到fl4的总贡献 
//	**********************************************
	ans=sum+max( max(fl1,fl2) , max(fl3,fl4 ) );//ans是sum+最大的贡献 
	cout<<ans<<endl;
}
signed main()
{
	int T=1;
//	cin>>T;
	while(T--) solve();
	return 0;
}