【题意】求有修改的区间第k大。
【分析】这个题目就是ZOJ2112(BZOJ1901)完全一样的;那个由于询问较少可以树状数组套主席树,树状数组套动态结点线段树,分块等;但是对于这题上面算法都会超内存(树状数组套主席树在G++下是TLE,其实是超内存引起的,HDU这个版本的G++对RE不太敏感);但是可以用线段树套treap+二分;但是这样时间复杂度为q*logn*logn*logn;空间为n*logn;刚好卡过;
但是有一个更好的方法:整体二分+树状数组。今天才学,其实很简单,知道单个查询在线段树上的二分后就很容易理解整体二分了,无非就是所有查询和修改都同时进行二分;通过二分值域以及把所有当前操作范围分治;分别分入已经完成修改的左边还是等待修改的右边即可;比较难说出来;看代码体会吧。这个时间复杂度为:(q+n)*logn*logn;空间复杂度:n+q;
由于一次查询出所有答案所以常数比n次logn*logai的要小很多;
【AC CODE(线段树套)】5584ms
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <set>
//#include <unordered_set>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 1e5+10;
struct NODE{
NODE *ch[2];
int r, v, sum, cnt;
}node[MAXN*50];
int a[MAXN], mi, mx, tol;
NODE *rt[MAXN<<1];
int cmp(const NODE *a, int x){
if(x == a->v) return -1;
return x<a->v?0:1;
}
void push_up(NODE *u){
u->sum = u->cnt;
if(u->ch[0]) u->sum += u->ch[0]->sum;
if(u->ch[1]) u->sum += u->ch[1]->sum;
}
NODE *newnode(int a){
node[tol].v = a,node[tol].ch[0] = node[tol].ch[1] = NULL, node[tol].r = rand();
node[tol].sum = node[tol].cnt = 1;
return &node[tol++];
}
void clear(NODE* &u)//清空树
{
u = NULL;
}
void rotate(NODE* &u, int d)//旋转,d = 0左旋,d = 1右旋
{
NODE *k = u->ch[d^1];
u->ch[d^1] = k->ch[d], k->ch[d] = u;
push_up(u), push_up(k); //更新sum,先更新u
u = k;
}
void insert(NODE* &u, int v)//插入键值v(不需要保证v是否存在)
{
if(!u) u = newnode(v);
else
{
int d = cmp(u,v);//不要使用cmp(),可能已经存在相同的v
if(-1 == d)//已经存在该点
{
u->cnt++;
u->sum++;
return;
}
insert(u->ch[d], v);
if(u->r < u->ch[d]->r) //如果儿子的r比当前大则需要旋转
rotate(u,d^1);//左儿子则右旋,右儿子左旋
}
push_up(u);//更新sum
}
void del(NODE* &u, int v)//删除键值v
{
int d = cmp(u,v);
if(~d)//没找到继续往下找
del(u->ch[d],v);
else
{
if(u->cnt > 1)
{
u->cnt--;
u->sum--;
return;
}
NODE *o = u;
if(u->ch[0] && u->ch[1])//左右儿子都存在
{
int d2 = u->ch[0]->r > u->ch[1]->r?1:0;//优先级大的儿子旋转为根
rotate(u,d2);
del(u->ch[d2],v);
}
else
{
if(!u->ch[0]) u = u->ch[1];//只有右儿子
else u = u->ch[0];//只有左儿子
//delete o;
//u = NULL;
}
}
if(u) push_up(u);
}
int upper(NODE *u, int v)//v在树中排第几,即有几个<=v
{
if(!u) return 0;
if(v == u->v) return u->cnt+(u->ch[0]?u->ch[0]->sum:0);
if(v < u->v) return upper(u->ch[0], v);
return upper(u->ch[1],v)+(u->ch[0]?u->ch[0]->sum:0)+u->cnt;
}
void pt(NODE *u)
{
if(NULL == u) return;
pt(u->ch[0]);
rep(i,0,u->cnt)
printf("%d\n", u->v);
pt(u->ch[1]);
}
inline int id(int x, int y){return x+y|x!=y;}
void bulid(int x, int y)
{
int u = id(x,y);
clear(rt[u]);
repe(i,x,y) insert(rt[u],a[i]);
if(x == y) return;
int m = (x+y)>>1;
bulid(x,m);
bulid(m+1,y);
}
int tmp;
void update(int x, int y, int p, int v)
{
if(x == y)
{
int u = id(x,y);
tmp = rt[u]->v;
rt[u]->v = v;
return;
}
int m = (x+y)>>1;
if(p <= m) update(x,m,p,v);
else update(m+1,y,p,v);
int u = id(x,y);
del(rt[u],tmp);
insert(rt[u],v);
}
int query(int x, int y, int ql, int qr, int v)
{
if(ql <= x && y <= qr)
{
int u = id(x,y);
return upper(rt[u],v);
}
int m = (x+y)>>1, ans = 0;
if(ql <= m) ans += query(x,m,ql,qr,v);
if(qr > m) ans += query(m+1,y,ql,qr,v);
return ans;
}
int n;
int sloved(int x, int y, int k)
{
int l = mi, r = mx, ans;
while(l <= r)
{
int m = (l+r)>>1;
if(query(0,n-1,x,y,m) >= k)
ans = m, r = m-1;
else l = m+1;
}
return ans;
}
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
while(~scanf("%d", &n))
{
int q;
mi = INF, mx = -1;
tol = 0;
rep(i,0,n)
{
scanf("%d%*c", &a[i]);
mi = min(mi,a[i]);
mx = max(mx,a[i]);
}
bulid(0,n-1);
scanf("%d", &q);
while(q--)
{
int op;
scanf("%d", &op);
if(1 == op)
{
int p,v;
scanf("%d %d", &p,&v);
p--;
update(0,n-1,p,v);
mi = min(mi,v);
mx = max(mx,v);
}
else
{
int x,y,k;
scanf("%d %d %d",&x, &y, &k);
x--,y--;
printf("%d\n", sloved(x,y,k));
}
}
}
return 0;
}
【AC CODE(整体二分+树状数组)】421ms
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = (1e5)+10, MAXIN = 1e4,MAXOUT = 1e4;
char buf[MAXIN], *ps = buf, *pe = buf+1;
inline void rnext(){
if(++ps == pe) pe = (ps = buf)+fread(buf,sizeof(char),sizeof(buf)/sizeof(char),stdin);
}
template <class T>
inline bool in(T &ans)
{
ans = 0;
T f = 1;
if(ps == pe) return false;
do{ rnext(); if('-' == *ps) f = -1;} while(!isdigit(*ps) && ps != pe);
if(ps == pe) return false;
do{ ans = (ans<<1)+(ans<<3)+*ps-48;rnext();}while(isdigit(*ps) && ps != pe);
ans *= f;
return true;
}
char bufout[MAXOUT], outtmp[50],*pout = bufout, *pend = bufout+MAXOUT;
inline void write(){ fwrite(bufout,sizeof(char),pout-bufout,stdout);pout = bufout;}
inline void out_char(char c){ *(pout++) = c;if(pout == pend) write();}
inline void out_str(char *s)
{
while(*s){ *(pout++) = *(s++); if(pout == pend) write(); }
}
template <class T>
inline void out_int(T x) {
if(!x){ out_char('0');return;}
if(x < 0) x = -x,out_char('-');
int len = 0;
while(x){ outtmp[len++] = x%10+48; x /= 10;}
outtmp[len] = 0;
for(int i = 0, j = len-1; i < j; i++,j--) swap(outtmp[i],outtmp[j]);
out_str(outtmp);
}
/*=====================================*/
struct NODE{
int op,x,y,k,id;
}qu[MAXN*3],q1[MAXN*3],q2[MAXN*3];
int a[MAXN],num[MAXN<<1],sum[MAXN],n;
inline int lowbit(int x){return x&-x;}//树状数组维护每个位置区间的数字个数
void update(int x, int v)
{
while(x <= n)
{
sum[x] += v;
x += lowbit(x);
}
}
int query(int x)
{
int ans = 0;
while(x > 0)
{
ans += sum[x];
x -= lowbit(x);
}
return ans;
}
int ans[MAXN];
void sloved(int x, int y, int l, int r)//第[x,y]之间的查询,已经被二分的值域[l,r]
{
if(l == r)
{
repe(i,x,y) if(2 == qu[i].op) ans[qu[i].id] = num[l];
return;
}
int m = (l+r)>>1,c1 = 0,c2 = 0;
repe(i,x,y)//划分[x,y]的操作进入左半值域[l,m]还是右半值域[m+1,r]
{
if(2 == qu[i].op)
{
int d = query(qu[i].y)-query(qu[i].x-1);//[x,y]中在当前查询之前,所要查询的区间中所有<=m的数字个数(循环顺序保证了现在树状数组中保存的都是之前的,且都<=m)
if(qu[i].k <= d) q1[c1++] = qu[i];//如果该查询的左边已经数字个数够了则进入左边
else//左边不够进入右边,由于分治所以k要减去左边的数量d
{
qu[i].k -= d;
q2[c2++] = qu[i];
}
}
else
{
if(qu[i].y <= m)//当前查询<=m进入左边,并更新树状数组
{
update(qu[i].x,qu[i].op);
q1[c1++] = qu[i];
}
else q2[c2++] = qu[i];
}
}
rep(i,0,c1) if(2 != q1[i].op) update(q1[i].x,-q1[i].op);//清空树状数组
memcpy(qu+x,q1,sizeof(NODE)*c1);
memcpy(qu+x+c1,q2,sizeof(NODE)*c2);
sloved(x,x+c1-1,l,m);
sloved(x+c1,y,m+1,r);
}
int main()
{
#ifdef SHY
freopen("d:\\1.txt", "r", stdin);
#endif
while(in(n))
{
int all = 0,tol = 0,query_cnt = 0;
repe(i,1,n)
{
in(a[i]),num[tol++] = a[i];
qu[++all] = {1,i,a[i]};
}
int q,op,x,y,k;
in(q);
rep(i,0,q)
{
in(op);in(x);in(y);
if(2 == op)
{
in(k);
qu[++all] = {2,x,y,k,query_cnt++};
}
else
{
num[tol++] = y;
qu[++all] = {-1,x,a[x]};
qu[++all] = {1,x,y};
a[x] = y;
}
}
sort(num,num+tol);
int cnt = unique(num,num+tol)-num;
repe(i,1,all)
{
if(2 != qu[i].op) qu[i].y = lower_bound(num,num+cnt,qu[i].y)-num;
}
sloved(1,all,0,cnt-1);
rep(i,0,query_cnt) out_int(ans[i]),out_char('\n');
}
write();
return 0;
}