bzoj 1314: River过河 树套树+单调队列

Description

ZY带N个小Kid过河,小KID分成两种:高一年级,高二年级,由于存在代沟问题,如果同一条船上高一年级生和高二年级生数量之差超过K,就会发生不和谐的事件.当然如果一条船上全是同一年级的,就绝对不会发生争执.现在ZY按小KID队列的顺序依次安排上船,并且不能让他们在过河时发生争执.对于当前等待上船的小KID来说,要么让他上船,要么将停在渡口的船开走,再让他上另一条船,同一条船上的人不过超过M人.为了让所有的小KID过河,在知悉小KID队列的情况下,最少需要多少条船.

Input

第一行给出N,M,K.含义如上所述 下行N行用来描述小KID的队列,每行一个字符”A”或者”L”

Output

最少需要多少条船

Sample Input

5 4 1

A

L

L

L

A

Sample Output

2

HINT

前三个人一条船,后两个人一条船
数据范围
30% 数据中1<=N<=1000
100%数据中1<=N<=250000,1<=M,K<=N

———————————————————————

这道题我们用单调队列维护相同一段的情况 用平衡树维护高一高二差不能超过k的情况

相同一段不能超过m的明显符合所选区间是在不断挪动的 所以用单调队列

而相差的话我们可以转换为类似扫描线的东西

先维护一波前缀和(sum) 是A就+1 L -1 这样方便查询差

转移方程 f【x】=miin (f【k】+1)

x-m<=k<=x  sum【x】-m<=sum【k】<=sum【x】+m

这样我们可以按坐标x扫 查询区间 max

但是这里的线段树我们下标是 sum

同一个下标可能有多个点 所以对于线段树的每个叶子节点我们都要建一棵平衡树

所以我们的操作是 插入 删除 区间取max (平衡树/线段树)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int M=250007,N=1<<19,inf=0x3f3f3f3f;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();}
    return ans*f;
}
multiset<int> q[N];
int x,n,m,k,sum[M];
int qq[N],ql,qr=1;
char str[M];
int s[N<<1],f[M];
void modify(int x){
    int v=q[x].size()?*q[x].begin():inf;
    for(s[x+=N]=v,x>>=1;x;x>>=1) s[x]=min(s[x<<1],s[x<<1^1]);
}
int query(int l,int r){
    int mn=inf;
    for(l+=N-1,r+=N+1;r-l!=1;l>>=1,r>>=1){
        if(~l&1) mn=min(mn,s[l+1]);
        if(r&1)  mn=min(mn,s[r-1]);
    }
    return mn;
}
int main(){
    memset(s,0x3f,sizeof(s));
    n=read(); m=read(); k=read();
    sum[0]=N/2;
    for(int i=1;i<=n;i++){
        scanf("%s",str+i);
        sum[i]=sum[i-1]+(str[i]==‘A‘?1:-1);
    }
    q[sum[0]].insert(0);
    modify(sum[0]);
    for(int i=1,v,j=1;i<=n;i++){
        while(ql<=qr&&(qq[ql]<i-m||str[qq[ql]+1]!=str[i])) ++ql;
        while(ql<=qr&&f[qq[qr]]>f[i-1]) --qr;
        qq[++qr]=i-1;
        v=query(max(1,sum[i]-k),min(N-2,sum[i]+k));
        f[i]=min(v,ql<=qr?f[qq[ql]]:inf)+1;
        if(i-m>=0){
            v=sum[i-m];
            q[v].erase(q[v].find(f[i-m]));
            modify(v);
        }
        v=sum[i];
        q[v].insert(f[i]);
        modify(v);
    }
    printf("%d\n",f[n]);
    return 0;
}

时间: 2024-10-07 05:28:46

bzoj 1314: River过河 树套树+单调队列的相关文章

bzoj 1314: River过河 优先队列

1314: River过河 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 26  Solved: 10[Submit][Status][Discuss] Description ZY 带N个小Kid过河,小KID分成两种:高一年级,高二年级,由于存在代沟问题,如果同一条船上高一年级生和高二年级生数量之差超过K,就会发生不和谐的 事件.当然如果一条船上全是同一年级的,就绝对不会发生争执.现在ZY按小KID队列的顺序依次安排上船,并且不能让他们在过河

bzoj 1901: Zju2112 Dynamic Rankings(树套树)

1901: Zju2112 Dynamic Rankings 经典的带修改求区间第k小值问题 树套树模板,我是用的线段树套splay实现的,而且用的数组模拟的,所以可能空间略大,bzoj过了,zoj过不了. 思路很简单,用线段树维护区间,用splay维护区间内的权值,然后询问的时候,二分答案key,然后在区间内找小于key的数有多少个. 贴上模板: #include<stdio.h> #include<string.h> #include<algorithm> #def

BZOJ 3196 二逼平衡树 树套树

题目大意:...BZOJ挂了自己看去 好吧既然BZOJ挂了我还是贴上来吧0.0 破服务器 维护一种数据结构,提供下列操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 其实一开始觉得这题是划分树主席树之类的 然后去了解了一下发现完全写不了... 后来才知道原来是树套树 以前想过线段树套树状数组 这数据范围别说树套树了连树状数组都开不开 正解应该是

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu

[BZOJ 3489] A simple rmq problem 【可持久化树套树】

题目链接:BZOJ - 3489 题目分析 “因为是OJ上的题,就简单点好了.”——出题人 真的..好..简单... 首先,我们求出每个数的前一个与它相同的数的位置,即 prev[i] ,如果前面没有相同的数,prev[i] = 0. 再求出每个数的后一个与它相同的数的位置,即 next[i], 如果后面没有相同的数,next[i] = n + 1. 这样,对于 l > prev[i], r < next[i] 的区间,i 这个数在区间中至多出现一次. 那么我们要求的就是:符合 prev[i]

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3

BZOJ 1396:识别子串 SA+树状数组+单调队列

1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 381  Solved: 243[Submit][Status][Discuss] Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodcookcooksgoodfood Sample Output 1 2 3 3

BZOJ 2141 排队 树套树

题目大意:给出一个数列,支持交换两个数字的操作,问每次操作之后的逆序对数量. 思路:数字比较大,先离散化.然后先求一次总逆序对,每次交换两个数字的时候用树套树维护一下逆序对的总数就可以了.. 好像树套树的常数略大,正解应该是分块.. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 20010 using namesp