题目描述

  给你你个序列,每次求区间第\(k\)小的数。

  本题中,如果一个数在询问区间中出现了超过\(w\)次,那么就把这个数视为\(n\)。

  强制在线。

  \(n\leq 100000,a_i<n,w\leq n\)

题解

  考虑整体二分。

  先看看离线要怎么做。

  现在我们要计算每个数对每个区间的贡献。

  对于每个询问区间和每种数,让这个区间最右边\(w\)个数对这个询问的贡献为\(1\),第\(w+1\)个数对这个询问的贡献为\(-w\)。

  这样每个数的贡献就是二维平面上的一个矩形。可以用扫描线+线段树解决。

  时间复杂度:\(O(n\log n)\)

  但问题是强制在线。

  可以把这棵线段树可持久化。

  时间复杂度不变。

  总时间复杂度:\(O(n\log^2 n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#include<utility>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
namespace sgt
{
struct node
{
int ls,rs,v;
};
node a[50000010];
int cnt;
int insert(int p1,int l,int r,int v,int L,int R)
{
int p=++cnt;
a[p]=a[p1];
if(l<=L&&r>=R)
{
a[p].v+=v;
return p;
}
int mid=(L+R)>>1;
if(l<=mid)
a[p].ls=insert(a[p].ls,l,r,v,L,mid);
if(r>mid)
a[p].rs=insert(a[p].rs,l,r,v,mid+1,R);
return p;
}
int query(int p,int x,int L,int R)
{
if(L==R)
return a[p].v;
int s=a[p].v;
int mid=(L+R)>>1;
if(x<=mid)
s+=query(a[p].ls,x,L,mid);
else
s+=query(a[p].rs,x,mid+1,R);
return s;
}
}
struct change
{
int x,y1,y2,k,w;
change(){}
change(int a,int c,int d,int e,int f)
{
x=a;
y1=c;
y2=d;
k=e;
w=f;
// printf("%d %d %d %d %d\n",x,y1,y2,k,w);
}
};
int cmp(change a,change b)
{
return a.x>b.x;
}
change c[1000010],c2[1000010];
int cnt;
int n,w,q,type;
int a[100010];
set<int> st[100010];
int rtcnt=0;
int ls[3000010];
int rs[3000010];
int crt;
vector<pii> d[3000010];
int build(int l,int r,int vl,int vr)
{
if(vl==vr)
return 0;
int rr=++rtcnt;
d[rr].push_back(pii());
int now=0;
int i;
int vm=(vl+vr)>>1;
int num=0;
int cnt1=0;
for(i=l;i<=r;i++)
{
if(i!=l&&c[i].x!=c[i-1].x&&num)
{
d[rr].push_back(pii(c[i-1].x,now));
num=0;
}
if(c[i].k<=vm)
{
now=sgt::insert(now,c[i].y1,c[i].y2,c[i].w,1,n);
num++;
cnt1++;
}
}
if(num)
d[rr].push_back(pii(c[r].x,now));
int l1=l,r1=l+cnt1;
for(i=l;i<=r;i++)
if(c[i].k<=vm)
c2[l1++]=c[i];
else
c2[r1++]=c[i];
for(i=l;i<=r;i++)
c[i]=c2[i];
ls[rr]=build(l,l+cnt1-1,vl,vm);
rs[rr]=build(l+cnt1,r,vm+1,vr);
return rr;
}
int get(vector<pii> &s,int x)
{
if(s.size()==1)
return 0;
if(x>s[1].first)
return 0;
int l=1,r=s.size()-1;
while(l<r)
{
int mid=(l+r+1)>>1;
if(x>s[mid].first)
r=mid-1;
else
l=mid;
}
return l;
}
int query(int rr,int l,int r,int k,int vl,int vr)
{
if(vl==vr)
return vl;
int p=get(d[rr],l);
int rt=d[rr][p].second;
int s=sgt::query(rt,r,1,n);
int vm=(vl+vr)>>1;
if(k<=s)
return query(ls[rr],l,r,k,vl,vm);
else
return query(rs[rr],l,r,k-s,vm+1,vr);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
scanf("%d%d%d%d",&n,&w,&q,&type);
int x,i;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=n;i>=1;i--)
{
x=a[i];
st[x].insert(i);
int ed2=n;
if(st[x].size()>=w+1)
{
int ed=n;
if(st[x].size()>=w+2)
{
set<int>::iterator p=st[x].end();
p--;
ed=*p-1;
st[x].erase(p);
}
set<int>::iterator p=st[x].end();
p--;
c[++cnt]=change(i,*p,ed,x,-w);
ed2=*p-1;
}
c[++cnt]=change(i,i,ed2,x,1);
}
sort(c+1,c+cnt+1,cmp);
int crt=build(1,cnt,0,n);
int l,r,k;
int last=0;
for(i=1;i<=q;i++)
{
scanf("%d%d%d",&l,&r,&k);
if(type)
{
l^=last;
r^=last;
k^=last;
}
last=query(crt,l,r,k,0,n);
printf("%d\n",last);
}
return 0;
}

【XSY2720】区间第k小 整体二分 可持久化线段树的更多相关文章

  1. POJ2104 K-th Number —— 区间第k小 整体二分

    题目链接:https://vjudge.net/problem/POJ-2104 K-th Number Time Limit: 20000MS   Memory Limit: 65536K Tota ...

  2. 静态区间第k小 - 整体二分

    蒟蒻终于学会整体二分啦! 思路 实现 丑陋无比的代码 #include <bits/stdc++.h> using namespace std; const int N = 200005; ...

  3. [bzoj3065] 带插入区间第k小值 [重量平衡树套线段树]

    题面 传送门 思路 发现强制在线了...... 本来可以树套树解决的问题,现在外层不能使用线段树了,拿什么替代呢? 我们需要一种支持单点插入.下套数据结构.数据结构上传合并复杂度最多单log,不能旋转 ...

  4. 51nod 1175 区间第k大 整体二分

    题意: 一个长度为N的整数序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有数中,第K大的数是多少. 分析: 仅仅就是一道整体二分的入门题而已,没听说过整体二分? 其实就是一个分治的函数 ...

  5. 【BZOJ2653】middle 二分+可持久化线段树

    [BZOJ2653]middle Description 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你一个 长度为n的序列s.回答Q个 ...

  6. BZOJ 4556(后缀数组+主席树求前驱后继+二分||后缀数组+二分+可持久化线段树)

    换markdown写了.. 题意: 给你一个1e5的字符串,1e5组询问,求\([l_1,r_1]\)的所有子串与\([l_2,r_2]\)的lcp 思路: 首先可以发现答案是具有单调性的,我们考虑二 ...

  7. 51Nod 1175 区间中第K大的数 (可持久化线段树+离散)

    1175 区间中第K大的数 基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题   一个长度为N的整数序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有 ...

  8. 2019.01.14 bzoj5343: [Ctsc2018]混合果汁(整体二分+权值线段树)

    传送门 整体二分好题. 题意简述:nnn种果汁,每种有三个属性:美味度,单位体积价格,购买体积上限. 现在有mmm个询问,每次问能否混合出总体积大于某个值,总价格小于某个值的果汁,如果能,求所有方案中 ...

  9. P5163 WD与地图(整体二分+权值线段树)

    传送门 细节要人命.jpg 这题思路太新奇了--首先不难发现可以倒着做变成加边,但是它还需要我们资瓷加边的同时维护强连通分量.显然加边之后暴力跑是不行的 然后有一个想法,对于一条边\((u,v)\), ...

随机推荐

  1. vue webpack打包 -webkit-box-orient 失效

    一行省略 overflow: hidden; white-space: nowrap; text-overflow: ellipsis; 超出两行省略 overflow: hidden; text-o ...

  2. Python全栈开发之路 【第十九篇】:Bootstrap

    一.下载和基本使用 官方地址:www.bootcss.com 二.响应式介绍 1.@meida 媒体查询 (1)响应式页面 为了页面能够适应不同工具的屏幕大小的限制,而开发的一种自适应页面,即 一次开 ...

  3. Oracle和Elasticsearch数据同步

    Python编写Oracle和Elasticsearch数据同步脚本 标签: elasticsearchoraclecx_Oraclepython数据同步    Python知识库 一.版本 Pyth ...

  4. 几个jdbc小技巧

    版本:jdk:1.6mysql-connector-5.0.8 1.如何用jdbc判断某个基本表是否存在 一种比较“笨”的方法:    try{         stmt.executeQuery( ...

  5. Python技术之书籍汇总

    近日,一直在学习Python,发现有关的书籍还是很多值得一读的,所以在此总结一下.以后慢慢去研读吧!!! Python入门 <Python编程快速上手——让繁琐工作自动化> 作者: [美] ...

  6. oracle创建表空间、创建用户、授权角色和导入导出用户数据

    使用数据库管理员身份登录 -- log as sysdba sqlplus / as sysdba; 创建临时表空间 -- create temporary tablespace create tem ...

  7. laravel服务容器

    laravel框架底层解析 本文参考陈昊<Laravel框架关键技术解析>,搭建一个属于自己的简化版服务容器.其中涉及到反射.自动加载,还是需要去了解一下. laravel服务容器 建立项 ...

  8. [转帖]NUMA架构的CPU -- 你真的用好了么?

    NUMA架构的CPU -- 你真的用好了么? 本文从NUMA的介绍引出常见的NUMA使用中的陷阱,继而讨论对于NUMA系统的优化方法和一些值得关注的方向. 文章欢迎转载,但转载时请保留本段文字,并置于 ...

  9. socket通信原理三次握手和四次握手详解

    对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1.         什么是TCP/IP.UDP?2.         Sock ...

  10. python学习笔记(10)--组合数据类型(字典类型)

    理解映射: 映射是一种键(索引)和值(数据)的对应.字典是键值对的集合,键值之间无序.用大括号表示{},和dict()创建,键值对用冒号:表示. {键:值,键:值,键:值} >>> ...