Grime Zoo CodeForces - 1411D

原题链接
考察:贪心
本蒟蒻想的是线性dp,时间复杂度太高否了(.),然后又想不到正解.
思路:
  参考了大佬的题解,就结果而言,最后要么是?序列0,1两极分布,要么是0,1混合分布.
  对于混合分布,对于每一个相邻的?,要么是0,1要么是1,0.假设两个相邻?之间有s0个0,s1个1.
  如果首尾填01那么这段对评论数的贡献是:s0x+s1x+x+外部对内部的贡献W
  如果首尾填10那么这段对评论数的贡献是:s0y+s1y+y+外部对内部的贡献W
  显然,如果x>y,我们需要将每一对相邻的?的01换成10,同理x<y.假定x>y,每个01都会被换成10,最后无法交换一定是不存在01,那么此时?构成的01序列一定是10两极分布,但是此时不能确定全填0或1就是最优的,所以需要枚举分界点.最后求出答案即可.
  比较好的计算思路是先将?全填1或0,对于每个替换进行+-操作.

Code

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 100010;
char s[N];//dp的时间复杂度太高 
int n,x,y,st[N][3],ed[N][3];
vector<int> v;
void init()
{
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='?')
		{
			st[i][2]++;
			v.push_back(i);
		}else st[i][s[i]-'0']++;
		for(int j=0;j<3;j++) st[i][j]+=st[i-1][j];
	}
	for(int i=n;i>=1;i--)
	{
		if(s[i]=='?') ed[i][2]++;
		else ed[i][s[i]-'0']++;
		for(int j=0;j<3;j++) ed[i][j]+=ed[i+1][j];
	}
}
void solve()
{
	LL ans = 0,res;
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='0') ans+=(LL)(st[i-1][1]+st[i-1][2])*y;
		else ans+=(LL)st[i-1][0]*x;
	}
	res = ans;
	if(x<=y)//填01更好 
	{
		for(int i=0;i<v.size();i++)
		{//枚举前i个填0 
			int j = v[i];
			res-=(LL)(st[j-1][0]+st[j-1][2])*x+(LL)ed[j+1][0]*y;
			res+=(LL)st[j-1][1]*y+(LL)(ed[j+1][1]+ed[j+1][2])*x;
			ans = min(res,ans);
		}
	}else{//填10 改成0 
		for(int i=v.size()-1;i>=0;i--) 
		{
			int j = v[i];
			res-=(LL)st[j-1][0]*x+(LL)(ed[j+1][0]+ed[j+1][2])*y;
			res+=(LL)(st[j-1][1]+st[j-1][2])*y+(LL)ed[j+1][1]*x;
			ans = min(res,ans);
		}
	}
	printf("%lld\n",ans);
}
int main()
{
	scanf("%s%d%d",s+1,&x,&y);
	n = strlen(s+1);
	init();
	solve();
	return 0;
}
posted @ 2021-06-07 11:29  acmloser  阅读(41)  评论(0编辑  收藏  举报