数据结构作业3-4(周)问题F:Turn off the light(关下灯)

题目描述

There are n lights aligned in a row. These lights are numbered 1 to n from left to right.
Initially some of the lights are turned on. Chiaki would like to turn off all the lights.
Chiaki starts from the p-th light. Each time she can go left or right (i.e. if Chiaki is at x, then she can go to x−1 or x+1) and then press the switch of the light in that position (i.e. if the light is turned on before, it will be turned off and vise versa).
For each p=1,2,…,n, Chiaki would like to know the minimum steps needed to turn off all the lights.

///一排中有n个灯,从左到右依次标号1到n,起初有些灯是亮着的。千明小姐想要关掉所有的灯。千明小姐从第p盏灯开始,每次她可以左右横跳一格来改变那个格子中灯的状态(关上的就打开,开着的就关上)。对于每个p=1,2,…,n,千明小姐想知道关闭所有灯所需的最小步骤。///

输入

There are multiple test cases. The first line of input is an integer T indicates the number of test cases. For each test case:
The first line contains an integer n (2≤n≤106) – the number of lights.
The second line contains a binary string s where si=1 means the i-th light is turned on and si=0 means i-th light is turned off.
It is guaranteed that the sum of all n does not exceed 107.

///有多个测试用例。输入的第一行是一个整数T,表示测试用例的数量。
对于每个测试用例:第一行包含整数n(2≤n≤106)——灯光数。
第二行包含一个二进制字符串s,其中si=1表示第i个灯打开,si=0表示第i个灯关闭。 题中会保证n<107。///

输出

最终的结果需要计算i*zi (i为1~字符串的长度的范围,zi为p在i的最小步骤),从1到n的和
输出和%(1e9+7)

举例:对于测试用例的111来说,p=1,则需要经过111>101>001>011>010>000 5个步骤;p=2,需经过111>011>001>000 3个步骤;p=3,需经过跟p=1一样的5个步骤,只不过倒过来。则结果为:15+23+3*5=26

样例输入

3
3
000
3
111
8
01010101

样例输出

0
26
432

你说这题该怎么办?我是真的不会。
你们自己去看大佬写的吧:运用了异或运算等知识

https://blog.csdn.net/BinGoo0o0o/article/details/81190093

https://blog.csdn.net/qq_18869763/article/details/81193622

最终代码:

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define 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;    
 	int i=1;    
 	while(i<=n)    
 	{   if(g[i])         
  		{if (fo==n+1) fo=i;lo=i;}        
  		i++;    
 	}    
 	if (fo>lo)     
 	{   i=1;        
  	    while(i<=n)        
  	    {   ret[i]=0;           
   		i++;        }         
  		return;    
 	    }    
 	for(i = 1; i <= n; i++)     
 	{   	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];    
 	}    
 	for( 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;            
  		if (preg[fw-1])ans+=2*(cnt[lw-1]-cnt[fw-1]);            
 		else ans+=2*(lw-fw-cnt[lw-1]+cnt[fw-1]);            
  		if (preg[lw-1]^preg[fw-1]^1) ans--;            
  		ret[i]=min(ret[i],ans);        
  		}         
  		else        
  		{   	int fw=fo,lw=lo;            
   			int ans=i-fo+lw-fw;            
   			if (preg2[fw-1])                 
   			ans += 2*(cnt2[i-1]-cnt2[fw-1]);            
   			else    ans+=2*(i-fw-cnt2[i-1]+cnt2[fw-1]);            
   			int x=preg2[i-1]^preg2[fw-1]^1;            
   			if (x==preg[i-1])                 
    				ans+=2*(cnt[lw-1]-cnt[i-1]);            
   			else  ans+=2*(lw-i-cnt[lw-1]+cnt[i-1]);            
   			if (x^preg[i-1]^preg[lw-1])   ans--;            
   			ret[i]=min(ret[i],ans);        
  		}    
	}
}
int main()
{   	scanf("%d",&T);    
 	while(T--)    
 	{   	scanf("%d",&n);        
  		scanf("%s",s+1);        
  		int i=1;        
  		while(i<=n)        
  		{   	g[i]=(s[i]=='1');            
   			i++;        
  		}        
  		work(g,ret,n);        
  		reverse(g+1,g+n+1);        
  		work(g,ret2,n);        
  		LL ans=0;        
  		for(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);    
 	}    
 return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值