码迷,mamicode.com
首页 > 其他好文 > 详细

[POI2011]LIZ-Lollipop

时间:2020-03-14 20:27:14      阅读:68      评论:0      收藏:0      [点我收藏+]

标签:ref   puts   return   register   const   如何   ons   lin   name   

[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;
}

祝大家学习愉快!

[POI2011]LIZ-Lollipop

标签:ref   puts   return   register   const   如何   ons   lin   name   

原文地址:https://www.cnblogs.com/Wendigo/p/12493641.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!