题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5412
解题思路:
官方题解:
In this problem, we can use BIT and Treap.
We use BIT for coding skill and each element of BIT is Treap.
First, for each coding skill, whose index can be added for BIT.
So each index can be inserted corresponding Treap.
The update operation can be done by deleting current index from old value’s structure and adding this to the new value.
The query operation can be done by deciding left bit from its binary representation.
This is possible by using BIT.
The number of corresponding skills can be counted by using Treap.
We count the index from l to r in Treap.
Time complexity:O(N⋅log2N)
题目大意:
给你一串数字,然后给你两种操作:
1:1 l v
操作一:把下标为l的点的值替换为v
2:2 l r k
操作二:在[l,r]区间求第k大值!
1.整体二分
对于初始数字,变为插入操作
按操作的时间顺序排列各个操作,对于修改操作拆为删除和加入操作:
1 删除之前插入的数字,2. 加入新的数字
接下来分治二分答案:
对于mid,如果插入或者删除的数字<=mid那么应该放到左区间,并且用树状数组(其他数据结构也行)
维护前X个位置有多少个数字在左边。
对于询问:如果l,r区间在左边的数字>=k那么答案在左边,否则答案在右边,并且更新K,k=k-这个区间去左边的数字个数
当low =high的时候,说明low就是答案了,只要更新询问还在low,low之间的答案即可。
复杂度分析:分治的深度是log(S)s是数据的范围。
每个询问每次被分到左边或者右边,会有log(s)次操作。每个插入或者删除也是。
但是要用树状数组维护多少个数字在左边,要log(n)次更新或者查询。
复杂度是n*log(s)*log(n)
AC代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;
#define maxn 300007
int tree[maxn];
void add(int p,int n){
for(;p<maxn;p+=p&(-p))
tree[p]+=n;
}
int query(int p){
int ans = 0;
for(;p>0;p-=p&(-p))
ans += tree[p];
return ans;
}
struct Node{
int l,r,k,ty,ans;
};
Node p[maxn];
int id1[maxn],id2[maxn];
void CDQ(int L,int R,int low,int high){
if(R < L) return ;
if(low == high ){
for(;L<=R;L++){
p[id1[L]].ans = low;
}
return ;
}
int mid = (low+high)/2,l=L,r=R,k,u;
for(int i = L;i <= R; i++){
u = id1[i];
if(p[u].ty == 2){
k = query(p[u].r) - query(p[u].l-1);
if(k >= p[u].k) id2[l++] = u;
else {
p[u].k -= k;
id2[r--] = u;
}
}
else if(p[u].k <= mid){
add(p[u].l,p[u].ty);
id2[l++] = u;
}
else id2[r--] = u;
}
for(int i = L; i <= R; i++){
u = id1[i];
if(p[u].ty != 2 && p[u].k <= mid) add(p[u].l,-p[u].ty);
}
for(k=L;k<l;k++)
id1[k] = id2[k];
for(r=R;k<=R;k++)
id1[k] = id2[r--];
CDQ(L,l-1,low,mid);
CDQ(l,R,mid+1,high);
}
int num[maxn];
int main(){
int n,q,t,cnt;
memset(tree,0,sizeof(tree));
while(scanf("%d",&n)!=EOF){
for(cnt=0;cnt<n;cnt++){
scanf("%d",&p[cnt].k);
p[cnt].ty = 1;
p[cnt].l = cnt+1;
num[cnt+1] = p[cnt].k;
}
scanf("%d",&q);
int ty,l,v;
for(int i = 0;i < q; i++,cnt++){
scanf("%d",&p[cnt].ty);
if(p[cnt].ty == 1){
scanf("%d%d",&l,&v);
p[cnt].ty = -1;
p[cnt].k = num[l];
p[cnt].l = l;
cnt++;
num[l] = v;
p[cnt].ty = 1;
p[cnt].k = v;
p[cnt].l = l;
}
else {
scanf("%d%d%d",&p[cnt].l,&p[cnt].r,&p[cnt].k);
}
}
for(int i = 0;i < cnt; i++)
id1[i] = i;
CDQ(0,cnt-1,0,1000000000);
for(int i = 0;i < cnt; i++){
if(p[i].ty == 2) printf("%d\n",p[i].ans);
}
}
return 0;
}
2.树状数组套平衡树
我们将树状数组的每一个节点代表对应的数字(需要将询问读入,然后把所有出现的数字离散化),平衡树中保存每一个数在序列中的下标。
修改:将原来序列中的数字在对应的树状数组套的平衡树中删除,再同理插入新的数字
询问:考虑答案的二进制表示,通过巧妙地运用树状数组的性质,我们可以从高位往地位贪心的构造答案,每次贪心在平衡树中查找对应区间的数字个数。
修改时间复杂度O(log^2N)
询问时间复杂度O(log^2N)构造答案+树状数组单点查询+平衡树查询
AC代码:
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define MAXN 100002
using namespace std;
using namespace __gnu_pbds;
struct query{int t,l,r,k;}q[MAXN];
//保存询问
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>bit[MAXN*2];
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>::iterator it;
//exSTL红黑树,G++ Only,低版本中null_type为null_mapped_type
int num[MAXN],n,ql,qr,k,oper,lsh[MAXN*2],H,ct;
//lsh为离散化数组 ,H为贪心构造答案的最高位
int lowbit(int x){return x&(-x);}
void add(int p,int num){
for(int i=p;i<=ct;i+=lowbit(i))
bit[i].insert(num);
}
//树状数组更新
void del(int p,int num){
for(int i=p;i<=ct;i+=lowbit(i))
bit[i].erase(num);
}
//树状数组删除
int pos(int num){return lower_bound(lsh,lsh+ct,num)-lsh+1;}
// 返回离散化下标
int query(int l,int r,int k){
int num=0;
for(int i=H;i;i>>=1){ //高位往地位贪心
int tmp=num+i;
if(tmp>ct)continue;
int kth=bit[tmp].order_of_key(r+1)-bit[tmp].order_of_key(l);
//平衡树查询
if(kth>=k)continue;
num=tmp,k-=kth;
}
return num;
}
//贪心构造答案
int main(){
int t;
while(scanf("%d",&n)!=EOF){
ct=0;
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
lsh[ct++]=num[i];
}
scanf("%d",&t);
for(int i=0;i<t;i++){
scanf("%d",&q[i].t);
if(q[i].t==1){
scanf("%d%d",&q[i].l,&q[i].k);
lsh[ct++]=q[i].k;
}else scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
}
sort(lsh,lsh+ct);
ct=unique(lsh,lsh+ct)-lsh;
for(int i=1;i<=ct;i++)bit[i].clear();
H=1;
while(H*2<ct)H*=2;
for(int i=0;i<t;i++)if(q[i].t==1)q[i].k=pos(q[i].k);
for(int i=1;i<=n;i++){
num[i]=pos(num[i]);
add(num[i],i);
}
for(int i=0;i<t;i++){
if(q[i].t==1){
del(num[q[i].l],q[i].l);
add(q[i].k,q[i].l);
num[q[i].l]=q[i].k;
}else printf("%d\n",lsh[query(q[i].l,q[i].r,q[i].k)]);
}
}
return 0;
}
3.线段树套treap
//#pragma warning (disable:4786)
//#pragma comment(linker,"/STACK:102400000,102400000") //手动扩栈
//#include <bits/stdc++.h>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <climits>
#include <ctype.h>
#include <queue>
#include <stack>
#include <vector>
#include <utility>
#include <deque>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps = 1e-9;
const double PI = acos(-1.00);
//#define PI 3.1415926535897932384626433832795
const double e = exp(1.0);
#define INF 0x3f3f3f3f
//#define INF 1e18
//typedef long long LL;
//typedef __int64 LL;
#define ONLINE_JUDGE
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
#define N 600010
#define M 100010
struct treap
{
int key,wht,count,sz,ch[2];
} tp[N*15];
int tree[N<<1];
int nodecount,root;
int IDX(int l,int r)
{
return l+r | l!=r;
}
void init()
{
tp[0].sz=0;
tp[0].wht=-INF;
nodecount=0;
root=0;
}
void update(int x)
{
tp[x].sz=tp[tp[x].ch[0]].sz+tp[x].count+tp[tp[x].ch[1]].sz;
}
void rotate(int &x,int t)
{
int y=tp[x].ch[t];
tp[x].ch[t]=tp[y].ch[!t];
tp[y].ch[!t]=x;
update(x);
update(y);
x=y;
}
void insert(int &x,int t)
{
if(! x)
{
x=++nodecount;
tp[x].key=t;
tp[x].wht=rand();
tp[x].count=1;
tp[x].ch[0]=tp[x].ch[1]=0;
}
else if(tp[x].key==t) tp[x].count++;
else
{
int k=tp[x].key<t;
insert(tp[x].ch[k],t);
if(tp[x].wht<tp[tp[x].ch[k]].wht) rotate(x,k);
}
update(x);
}
void erase(int &x,int t)
{
if(tp[x].key==t)
{
if(tp[x].count==1)
{
if(! tp[x].ch[0] && ! tp[x].ch[1])
{
x=0;
return;
}
rotate(x,tp[tp[x].ch[0]].wht<tp[tp[x].ch[1]].wht);
erase(x,t);
}
else tp[x].count--;
}
else erase(tp[x].ch[tp[x].key<t],t);
update(x);
}
int select(int x,int t)
{
if(! x) return 0;
if(tp[x].key>t) return select(tp[x].ch[0],t);
return tp[x].count+tp[tp[x].ch[0]].sz+select(tp[x].ch[1],t);
}
int a[N],b[N],ord[M][5],lb;
int n,m,tt;
int search(int x)
{
int l=1,r=b[0],mid;
while (l<=r)
{
mid=(l+r)>>1;
if(b[mid]==x) return mid;
if(b[mid]<x) l=mid+1;
else r=mid-1;
}
}
void treeinsert(int l,int r,int i,int x)
{
insert(tree[IDX(l,r)],x);
if(l==r) return;
int m=(l+r)>>1;
if(i<=m) treeinsert(l,m,i,x);
else treeinsert(m+1,r,i,x);
}
void treedel(int l,int r,int i,int x)
{
erase(tree[IDX(l,r)],x);
if(l==r) return;
int m=(l+r)>>1;
if(i<=m) treedel(l,m,i,x);
else treedel(m+1,r,i,x);
}
int query(int l,int r,int x,int y,int k)
{
if(l==r) return l;
int m=(l+r)>>1;
int ans=select(tree[IDX(l,m)],y)-select(tree[IDX(l,m)],x);
if(ans>=k) return query(l,m,x,y,k);
return query(m+1,r,x,y,k-ans);
}
int main ()
{
while (~scanf("%d",&n))
{
b[0]=1;
lb=0;
memset(tree,0,sizeof(tree));
init();
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
b[++lb]=a[i];
}
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
int op;
int x,y,c;
scanf("%d",&op);
if(op == 2)
{
scanf("%d %d %d",&x,&y,&c);
ord[i][1]=1;
ord[i][2]=x;
ord[i][3]=y;
ord[i][4]=c;
}
else
{
scanf("%d %d",&x,&y);
ord[i][1]=2;
ord[i][2]=x;
ord[i][3]=y;
b[++lb]=y;
}
}
sort(b+1,b+1+lb);
for(int i=1; i<=lb; i++)
if(b[i]!=b[b[0]]) b[++b[0]]=b[i];
for(int i=1; i<=n; i++)
{
a[i]=search(a[i]);
treeinsert(1,b[0],a[i],i);
}
for(int i=1; i<=m; i++)
{
if(ord[i][1]==1)
printf("%d\n",b[query(1,b[0],ord[i][2]-1,ord[i][3],ord[i][4])]);
else
{
treedel(1,b[0],a[ord[i][2]],ord[i][2]);
a[ord[i][2]]=search(ord[i][3]);
treeinsert(1,b[0],a[ord[i][2]],ord[i][2]);
}
}
}
return 0;
}