zsyzlzy

导航

 

【题意】
公司动态排名开发了一种新型计算机,不再满足于简单地找到给定N个数的第k个最小数。他们开发了一个更强大的系统,对于N个数a[1],a[2],…,a[N],你可以问它:a[i],a[i+1],…,a[j]的第k个最小数是多少?(i<=j,0<k<=j-i+1)。你甚至可以改变一些a[i]的值,并继续查询。
你的任务是为这台计算机编写一个程序

  • 从输入中读取N个数字(1 <= N <= 50000)
  • 处理输入的M指令(1 <= M <= 10000)。这些指令包括查询a[i],a[i+1],…,a[j]的第k个最小数,或将a[i]改为t。
    【输入】
    输入的第一行是单个数字X(0<X<=4),即输入的测试用例的数量。然后每个X块代表一个测试用例。
    每个块的第一行包含两个整数N和M,表示N个数和M个指令。接下来是N行。第i+1行代表数字a[i]。然后是M行,格式如下
    Q i j k
    或者
    C i t
    它表示查询a[i],a[i+1],…,a[j]的第k个数,并分别将a[i]变为t。保证在任何操作时间,任何数字a[i]是小于1000000000的非负整数。
    两个连续测试用例之间没有空行。
    【输出】
    对于每个查询操作,输出一个整数来表示结果。(即a[i],a[i+1],…,a[j]的第k个最小数)
    两个连续测试用例之间没有空行。
    【输入样例】
    2
    5 3
    3 2 1 4 7
    Q 1 4 3
    C 2 6
    Q 2 5 3
    5 3
    3 2 1 4 7
    Q 1 4 3
    C 2 6
    Q 2 5 3

【输出样例】
3
6
3
6

整体分治模板题

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
using namespace std;
const int N=70010;

void qr(int &x) {
	char c=g;x=0;
	while(!isdigit(c))c=g;
	while(isdigit(c))x=x*10+c-'0',c=g;
}
void write(int x) {
	if(x/10)write(x/10);
	putchar(x%10+'0');
}
void pri(int x) { write(x); puts("");}

struct rec { int op,x,y,z;}q[N],lq[N],rq[N];//操作序列(按时间排列) 
int T,t,p,n,m,a[N],b[N],tot,c[N],ans[N];
//数据总数,操作数,询问数。 
int ask(int x) {
	int y=0;
	for(	;x;x&=x-1) y+=c[x];
	return y;
}

int up(int x,int y) {
	for(	;x<=n;x+=x&-x) 
		c[x]+=y;
}

void clear(int x) {
	for(	;x&&c[x];x+=x&-x)
		c[x]=0;
}

int sta[N],top;
//整体分治其实就是把原问题分成若干个相同的子问题。
//当问题缩小到一定规模时,答案自然水落石出。 
void solve(int l,int r,int st,int ed) {
	if(st>ed) return ;
	if(l==r) {
		for(int i=st;i<=ed;i++)
			ans[q[i].op]=b[l];
		return ;
	}
	int mid=(l+r)>>1,val=b[mid],lt=0,rt=0;
	for(int i=st;i<=ed;i++) {
		if(!q[i].op) {
			if(q[i].y<=val) up(q[i].x,q[i].z),lq[++lt]=q[i],sta[++top]=q[i].x;
			else rq[++rt]=q[i];
		}
		else {
			int cnt=ask(q[i].y)-ask(q[i].x-1);
			if(q[i].z<=cnt) lq[++lt]=q[i];
			else q[i].z-=cnt,rq[++rt]=q[i];
		}
	}
	while(top) clear(sta[top--]);//还原树状数组 
	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(l,mid,st,st+lt-1);
	solve(mid+1,r,st+lt,ed);
}

int main() {
	qr(T);
	while(T--) {
		qr(n);qr(m);t=p=0;
		for(int i=1;i<=n;i++) {
			qr(a[i]);
			q[++t]=(rec){0,i,a[i],1};
		}
		memcpy(b,a,(n+1)<<2); tot=n;
		for(int i=1;i<=m;i++) {
			char s[5];scanf("%s",s);
			int l,r,k;qr(l);qr(r);
			if(s[0]=='C') {//本题核心思想:把修改拆成 减去原来的数 + 加上现在的数 两个部分 
				q[++t]=(rec){0,l,a[l],-1};
				q[++t]=(rec){0,l,a[l]=r,1};
				b[++tot]=r;
			}
			else qr(k),q[++t]=(rec){++p,l,r,k};
		}
		sort(b+1,b+tot+1); top=1;
		for(int i=2;i<=tot;i++)
			if(b[i]!=b[top]) b[++top]=b[i];//答案一定在出现的数中 
		tot=top; top=0;
		solve(1,tot,1,t);
		for(int i=1;i<=p;i++) pri(ans[i]);
	}
	return 0;
}
	
posted on 2019-10-16 10:53  zsyzlzy  阅读(122)  评论(0编辑  收藏  举报