2018 Multi-University Training Contest 1——1010.Turn Off The Light

规律:如果从1开始往右走的话,如果从(1~i)经过的零的个数和有奇数个零的话(那么i与下个数必定要有一个来回),可以手动模拟样例。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N=1010000;
const int mod = 1e9+7;
int n,T;
bool f[N],g[N],preg[N],preg2[N];
char s[N];
int ret[N],ret2[N],cnt[N],cnt2[N];
void work(bool *g,int *ret,int n) {
	for(int i = 1; i <= n; i++) ret[i]=1<<30;
	int fo=n+1,lo=-1;
	for(int i = 1; i <= n; i++) {
		if (g[i]) {
			if (fo==n+1) fo=i;
			lo=i;
		}
	}
	if (fo>lo) {
		for(int i = 1; i <= n; i++) ret[i]=0;
		return;
	}
	for(int i = 1; i <= n; i++) {
	    //preg前缀0的个数是奇数(1)还是偶数(0)
        //preg2前缀1的个数是奇数(1)还是偶数(0)
        //cnt[i]为[1,i]中preg为奇数的位置的个数
        //cnt2[i]为[1,i]中preg2为奇数的位置的个数
		preg[i]=preg[i-1]^g[i]^1;
		preg2[i]=preg2[i-1]^g[i];
		cnt[i]=cnt[i-1]+preg[i];
		cnt2[i]=cnt2[i-1]+preg2[i];
	//	cout<<i<<" "<<cnt[i]<<endl;
	}
	for(int i = 1; i <= lo; i++) {
		if (i<=fo) {
			int fw=i,lw=lo;
			if (i==lo) {
				ret[i]=min(ret[i],3);
				continue;
			}
			int ans=lw-fw;
			//前缀为0个数为奇数,出发点在fw,i的值固定为0,需要来回的次数。
			if (preg[fw-1]) ans+=2*(cnt[lw-1]-cnt[fw-1]);
			else ans+=2*(lw-fw-cnt[lw-1]+cnt[fw-1]);
			//区间总共偶数个零ans--,出发点在i,区间偶数个零停在lo-1处,最后一步不走。
			if (preg[lw-1]^preg[fw-1]^1) ans--;
			ret[i]=min(ret[i],ans);
		} else {
		    //[fw,i]为第一段,[i,lo]为第二段
		    //先走到最左,走过一遍后,被走过的位置1变成0,0变成1,所以用
			int fw=fo,lw=lo;
			int ans=i-fo+lw-fw;
			//到达最左后出发点为fw,fw的值固定为0,故同上操作。
			if (preg2[fw-1]) ans += 2*(cnt2[i-1]-cnt2[fw-1]);
			else ans+=2*(i-fw-cnt2[i-1]+cnt2[fw-1]);

            //到达此处时已经到达i处,且i以前全为0,但i自己的值不固定
			int x=preg2[i-1]^preg2[fw-1]^1;
			//前缀0的个数要与前一段区间的奇偶行相反,
			//x为1则代表从i的地方出发i的位置上为1等同于preg[i-1]为奇数的情况,都要在i-1与i之间来回一次(即ans+=2),
			//否则i的位置上为0等同于preg[i-1]为0的情况
			if (x==preg[i-1]) ans+=2*(cnt[lw-1]-cnt[i-1]);
			else ans+=2*(lw-i-cnt[lw-1]+cnt[i-1]);
			//若x=1,则第一段区间零的个数为偶数,则第二段区间零的个数为偶数,和为偶数;
			//若x=0,则第一段区间零的个数为奇数,则第二段区间零的个数为奇数,和为偶数。
			if (x^preg[i-1]^preg[lw-1]) ans--;
			ret[i]=min(ret[i],ans);
		}
	}
}
void solve() {
	scanf("%d",&n);
	scanf("%s",s+1);
	for(int i = 1; i <= n; i++) g[i]=(s[i]=='1');
	work(g,ret,n);
	reverse(g+1,g+n+1);
	work(g,ret2,n);
	LL ans=0;
	for(int i = 1; i <= n; i++) {
		ret[i]=min(ret[i],ret2[n+1-i]);
		ans=(ans+(LL)i*ret[i])%mod;
	}
	printf("%lld\n",ans);
}
int main() {
#ifdef LOCAL
    freopen("input.txt","r",stdin);
#endif // LOCAL
	for (scanf("%d",&T); T; T--) solve();
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值