标签:ref puts return register const 如何 ons lin name
前置知识:
破脑术开脑洞。
给定一个长度为 \(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\) 的区间呢:
满足 \(a_L=2\) 或 \(a_R=2\),所以和为 \(x-2\) 的区间是 \([a_L+1,a_R]\) 或 \([a_L,a_R-1]\)。
否则肯定 \(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;
}
祝大家学习愉快!
标签:ref puts return register const 如何 ons lin name
原文地址:https://www.cnblogs.com/Wendigo/p/12493641.html