题目大意:有m个操作,操作有两种类型:1是在x,y位置放一个值v,2是询问x1,y1 到x2,y2之间第k大的值是几。
解法:第K大可以用整体二分,问题是这里是矩形区间内的第k大,而不是线性序列上的第k大。
还是一样的,回忆整体二分:二分答案区间,将操作分开,递归处理左答案区间和右答案区间的操作。插入操作还是一样的,值小于等于mid 放左边,值大于mid 放右边,对于询问操作,我们要求出这个询问操作的矩形区间内值大于mid的个数,以此来判断这个询问操作该放左边还是右边,对于线性序列,只需要用树状数组搞一下就行了。
这里仔细分析一下:其实就是求当前这些操作序列中:时间在该操作前,值 > mid,且坐标在这个询问范围内的插入操作有多少个。把值 > mid的操作筛出来,可以发现这就是一个三维偏序问题,因此整体二分中操作序列的划分判断需要用到cdq分治,求出个数之后再将操作划分然后分别解决左边和右边。
然后注意一下可能不存在第k大,剩下的就和整体二分一样了
时间复杂度:因为每一个子问题要套cdq分治,时间复杂度为n * (log n) ^ 3,空间复杂度为o (n)
#include<iostream>
using namespace std;
const int maxn = 3e6 + 10;
#include<stdio.h>
#define lowbit(i) (i & (-i))
const int inf = 1e9;
int n,m;
struct node{
int op,x,y,val,id,type;
bool operator < (const node & rhs) const {
if(x == rhs.x) {
if(y == rhs.y) return id < rhs.id;
else return y < rhs.y;
}
else return x < rhs.x;
}
}q[maxn],lq[maxn],rq[maxn],tt[maxn],tmp[maxn];
int sum[maxn],k[maxn],ans[maxn],C[maxn],tim;
void modify(int p,int v) {
for(; p <= n; p += lowbit(p)) sum[p] += v;
}
int ask(int p) {
int res = 0;
for(; p > 0; p -= lowbit(p)) res += sum[p];
return res;
}
void cdq(int l,int r,int mx) {
if(l == r) return;
int mid = l + r >> 1;
cdq(l,mid,mx);cdq(mid + 1,r,mx);
int top = 0;
for(int i = l,j = mid + 1; i <= mid || j <= r;) {
if(i <= mid && (j > r || tt[i] < tt[j])) {
if(tt[i].op == 0 && tt[i].val > mx)
modify(tt[i].y,1);
tmp[++top] = tt[i++];
}
else {
if(tt[j].op > 0) {
if(tt[j].type == 2)
k[tt[j].op] += ask(tt[j].y);
if(tt[j].type == 3)
k[tt[j].op] -= ask(tt[j].y);
}
tmp[++top] = tt[j++];
}
}
for(int i = l; i <= mid; i++) {
if(tt[i].op == 0 && tt[i].val > mx)
modify(tt[i].y,-1);
}
for(int i = 1; i <= top; i++)
tt[i + l - 1] = tmp[i];
}
void init(int l,int r) {
for(; l <= r; l++) {
k[q[l].op] = 0;
tt[l] = q[l];
}
}
void solve(int vl,int vr,int st,int ed) {
if(st > ed) return;
if(vl == vr) {
init(st,ed);cdq(st,ed,vl - 1);
for(int i = st; i <= ed; i++) {
if(q[i].op > 0 && q[i].val <= k[q[i].op]) //(注意)判断第K大存在的条件
ans[q[i].op] = vl;
else ans[q[i].op] = -1;
}
return ;
}
int mid = vl + vr >> 1,lt = 0,rt = 0;
init(st,ed);
cdq(st,ed,mid);
for(int i = st; i <= ed; i++) {
if(q[i].op == 0) {
if(q[i].val <= mid) lq[++lt] = q[i];
else rq[++rt] = q[i];
}
else {
if(k[q[i].op] >= q[i].val) rq[++rt] = q[i];
else {
q[i].val -= k[q[i].op];
lq[++lt] = q[i];
}
}
}
for(int i = 1; i <= lt; i++) {
q[st + i - 1] = lq[i];
}
for(int i = 1; i <= rt; i++) {
q[st + lt + i - 1] = rq[i];
}
solve(vl,mid,st,st + lt - 1);
solve(mid + 1,vr,st + lt,ed);
}
int main() {
scanf("%d%d",&n,&m);
int x,y,a,b,op,v,tot = 0,cnt = 0;
for(int i = 1; i <= m; i++) {
scanf("%d%d%d",&op,&x,&y);
if(op == 1) {
scanf("%d",&v);
q[++tot] = (node){0,x,y,v,i,0};
}
if(op == 2) {
scanf("%d%d%d",&a,&b,&v);
cnt++;
q[++tot] = (node){cnt,x - 1,y - 1,v,i,2};
q[++tot] = (node){cnt,x - 1,b,v,i,3};
q[++tot] = (node){cnt,a,b,v,i,2};
q[++tot] = (node){cnt,a,y - 1,v,i,3};
}
}
solve(1,inf,1,tot);
for(int i = 1; i <= cnt; i++) {
if(ans[i] != -1) printf("%d\n",ans[i]);
else printf("NAIVE!ORZzyz.\n");
}
return 0;
}