Loading

[POI2011]LIZ-Lollipop

[POI2011]LIZ-Lollipop

前置知识:

破脑术开脑洞。


[POI2011]LIZ-Lollipop

给定一个长度为 \(n\) 的序列 \(a\{n\}(a_i\in{1,2})\)\(m\) 次询问求 \(k\) 是否可以表示成连续某段序列 \(a_L\sim a_R\) 的和。如果可以,输出 \(L\)\(R\)。否则,输出 \(\texttt{NIE}\)。(注:读入序列用字符串,\(\texttt{T}\) 表示 \(2\)\(\texttt{W}\) 表示 \(1\))。

数据范围:\(1\le n,m\le 10^6\)\(1\le k\le 2\times 10^6\)


如果不是思维难度的原因,这题是橙色的吧。


首先想到区间和就要先把前缀和算出来。

逆推做。如果有一个区间 \([L,R]\) 的和为 \(x\),那么如何得出和为 \(x-2\) 的区间呢

  1. 满足 \(a_L=2\)\(a_R=2\),所以和为 \(x-2\) 的区间是 \([a_L+1,a_R]\)\([a_L,a_R-1]\)

  2. 否则肯定 \(a_L=a_R=1\),那么和为 \(x-2\) 的区间就是 \([a_l+1,a_R-1]\)

所以只需要找出最大的奇数区间和 \(x\) 和最大的偶数区间和 \(x\) 即可。

然后逆推别的,可以循环或者递归,至于找最大奇偶数区间和,就不必多说了。


\(\texttt{code}\)

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

//&Start
#define lng long long
#define lit long double
#define re register
#define kk(i,n) " \n"[i==n]
const int inf=0x3f3f3f3f;
const lng Inf=0x3f3f3f3f3f3f3f3f;

//&Data
const int N=1e6;
int n,m,a[N+10],mo=-1,me=-1;
int l[(N<<1)+10],r[(N<<1)+10],sum[N+10];
//l[i] 表示区间和为 i 的区间左端点,r[i] 表示右端点(不开双倍空间也能得不少分吧)
char c[N+10];

//&Solve
void solve(int x){ //循环实现逆推
	if(x<=2) return;
	if(a[l[x]]==2) l[x-2]=l[x]+1,r[x-2]=r[x];
	else if(a[r[x]]==2) l[x-2]=l[x],r[x-2]=r[x]-1;
	else l[x-2]=l[x]+1,r[x-2]=r[x]-1;
	solve(x-2);
}

//&Main
int main(){
	memset(l,-1,sizeof l);
	memset(r,-1,sizeof r);
	scanf("%d%d%s",&n,&m,c+1);
	//-----------------魔芋分割线---------------------------
	for(int i=1;i<=n;i++) a[i]=(c[i]=='T'?2:1);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; //求前缀和
	//-----------------魔芋分割线---------------------------
	if(sum[n]&1) mo=sum[n],l[mo]=1,r[mo]=n;
	else me=sum[n],l[me]=1,r[me]=n; //因为所有数的和肯定是该奇偶性下最大的
	// printf("varque %d %d\n",mo,me);
	//-----------------魔芋分割线---------------------------
	int lf=-1,ri=-1;
	for(int i=1;i<=n;i++)if(a[i]==1){lf=i;break;}
	for(int i=n;i>=1;i--)if(a[i]==1){ri=i;break;}//找1,得出另外奇偶性下最大的区间和
	//-----------------魔芋分割线---------------------------
	int tmp=-1,lt=-1,rt=-1;
	if(lf!=-1&&sum[n]-sum[lf]>tmp)
		tmp=sum[n]-sum[lf],lt=lf+1,rt=n;
	if(ri!=-1&&sum[ri-1]>tmp)
		tmp=sum[ri-1],lt=1,rt=ri-1;
	//-----------------魔芋分割线---------------------------
	if(sum[n]&1) me=tmp,l[me]=lt,r[me]=rt;
	else mo=tmp,l[mo]=lt,r[mo]=rt;
	// printf("varque %d %d\n",mo,me);
	//-----------------魔芋分割线---------------------------
	solve(mo); //逆推
	solve(me);
	//-----------------魔芋分割线---------------------------
	for(int i=1,x;i<=m;i++){
		scanf("%d",&x);
		if(l[x]==-1&&r[x]==-1) puts("NIE"); //没有方案
		else printf("%d %d\n",l[x],r[x]); //输出
	}
	return 0;
}


祝大家学习愉快!

posted @ 2020-03-14 19:04  George1123  阅读(90)  评论(0编辑  收藏  举报