zoukankan      html  css  js  c++  java
  • [LOJ2736] [JOISC 2016 Day 3] 回转寿司 (分块+堆)

    [LOJ2736] [JOISC 2016 Day 3] 回转寿司 (分块+堆)

    题面

    给出一个有n 个点的环,环上各点有一个初始权值 (a_i)

    给出 Q 个询问,每次询问给出一个区间 [l,r]和一个值 A,对于 A 的变动定义如下

    for (int i = l; i <= r; i++) if(a[i] > A) swap(a[i],A);
    

    对于每个询问,回答遍历完区间[l,r]后 A的最终值。

    分析

    这种交换看起来很难用一般的数据结构维护,考虑对序列分块。

    首先,我们发现如果一个块里面所有的数都<A,那么什么变化都不会发生。否则每个块中最大的数会被A替换掉,A变成(max(A,max(a_i)) (iin [l,r]))。因此对每个块内的数维护一个大根堆,只有堆顶比当前A小的时候才会整块修改。

    一般分块的套路,整块修改的时候只需要打一个标记,两端的小块直接按题面模拟遍历即可。遍历两端的时候要把标记下推,同时重构两端的块

    考虑标记下推,我们发现对于两个不同的标记Ax,Ay,若Ax<Ay,最大的数会被替换成Ax。所以我们每个块用一个小根堆存储标记,遍历重构的块的时候若a[i]>A,把原来的a[i]插入小根堆,然后swap(A,a[i])。可以证明,这和多次遍历块来交换每个标记是一样的。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring> 
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #define maxn 400000
    #define maxb 1000
    using namespace std;
    inline void qread(int &x) {
    	x=0;
    	int sign=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') sign=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=x*10+c-'0';
    		c=getchar();
    	}
    	x=x*sign;
    }
    inline void qprint(int x) {
    	if(x<0) {
    		putchar('-');
    		qprint(-x);
    	} else if(x==0) {
    		putchar('0');
    		return;
    	} else {
    		if(x>=10) qprint(x/10);
    		putchar('0'+x%10);
    	}
    }
    
    int n,m;
    int a[maxn+5];
    
    int bsz;
    int bel[maxn+5];
    inline int lb(int id){
    	return bsz*(id-1)+1;
    }
    inline int rb(int id){
    	return bsz*id>=n?n:bsz*id; 
    }
    priority_queue<int>num[maxb+5];//大根堆,存储每一块中的数 
    priority_queue<int,vector<int>,greater<int> >mark[maxb+5] ;//小根堆,存储每一个块上替换标记 
    //从小到大是因为序列中的数会被优先换成小的标记
    void rebuild(int id){//重构每个块,不管是整块替换还是部分替换,都要调用rebuild 
    	while(!num[id].empty()) num[id].pop();
    	for(int i=lb(id);i<=rb(id);i++){
    		num[id].push(a[i]);
    	}
    }
    void push_down(int id){
    	if(!mark[id].empty()){
    		for(int i=lb(id);i<=rb(id);i++){
    			int v=mark[id].top();
    			if(v<a[i]){
    				swap(a[i],v);
    				mark[id].pop();
    				mark[id].push(v);//把a[i]与v交换 
    			}
    		}
    		while(!mark[id].empty()) mark[id].pop();
    		rebuild(id); 
    	}
    	
    } 
    
    int update(int l,int r,int A){
    	push_down(bel[l]);//非整块操作,必须下推标记 
    	for(int i=l;i<=min(r,rb(bel[l]));i++){
    		if(A<a[i]) swap(a[i],A);
    	}
    	rebuild(bel[l]);
    	for(int i=bel[l]+1;i<bel[r];i++){
    		int v=num[i].top();
    		if(v>A){ //如果最大值比A小,就不替换了 
    			num[i].pop();
    			num[i].push(A);
    			mark[i].push(A);
    			swap(A,v);
    			//整块修改的时候只需要替换最大值,剩下的操作push_down的时候完成
    		}
    	}
    	if(bel[l]!=bel[r]){
    		push_down(bel[r]);
    		for(int i=lb(bel[r]);i<=r;i++){
    			if(A<a[i]) swap(a[i],A);
    		}
    		rebuild(bel[r]);
    	}
    	return A;
    }
    int main(){
    	int l,r,A;
    	qread(n);
    	qread(m);
    	for(int i=1;i<=n;i++) qread(a[i]);
    	bsz=sqrt(n);
    	for(int i=1;i<=n;i++){
    		bel[i]=(i-1)/bsz+1;
    		num[bel[i]].push(a[i]); 
    	}
    	for(int i=1;i<=m;i++){
    		qread(l);
    		qread(r);
    		qread(A);
    		if(l<=r){
    			A=update(l,r,A);
    		}else{
    			A=update(l,n,A);
    			A=update(1,r,A);
    		}
    //		printf("db:"); 
    		qprint(A);
    		putchar('
    ');
    	} 
    }
    
    
  • 相关阅读:
    存储结构接收数组
    oracle数据库sql根据查看执行计划优化sql--走不走索引
    多线程--Thread
    java常用集合族谱
    设计模式之二 适配模式
    Tomcat优化问题
    设计模式之一
    C++虚函数表,虚表指针,内存分布
    设计模式
    linux环境下的时间编程
  • 原文地址:https://www.cnblogs.com/birchtree/p/11323368.html
Copyright © 2011-2022 走看看