zoukankan      html  css  js  c++  java
  • CF896E Welcome home, Chtholly(第二分块)

    给定长度 (n) 的序列 (a)(m) 个操作:

    • 对于区间 ([l,r]) 中的大于 (x) 的数减去 (x)
    • 查询区间 ([l,r]) 中等于 (x) 的数的个数

    (a_ile 10^5,n,mle 10^5)

    看到值域相当小,于是可以在这上面入手
    考虑对序列分块,则每块的最大值的和不超过 (O(nsqrt{n}))
    那么对于整块的情况,设这一块的最大值为 (max),要对其中大于 (x) 的数减去 (x),则:

    • 如果 (2x<max),则将所有的 ([1,x]) 中的数移动到 ([x+1,2x]) 中,并对于每个块维护一个 (tag) 表示他向右移动了多少。设 ([1,x]) 中的数有 (k) 个,则花费 (O(kx)) 的复杂度把这个块的 (max) 降低了 (x)
    • 如果 (2xge max),则将所有的 ([x+1,max]) 的数移动到 ([1,max-x]) 中。设 ([x+1,max]) 中的数有 (k) 个,则花费 (O(k(max-x))) 的复杂度把这个块的 (max) 降低了 (max-x)

    对于散块直接重构

    于是可以对每个块的每个值分别搞一个并查集,并记录一下大小

    然后因为所有块的最大值之和有保证,所以复杂度就对了

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline int read(){
    	reg int x=0;reg int y=1;reg char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')y=0;c=getchar();}
    	while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
    	return y?x:-x;
    }
    #define N 100522
    #define B 322
    int blocksize,blocknum;
    int n,a[N];
    int belong[N],left[B],right[B];
    struct Node{int root,num;};
    Node v[B][N];
    int pos[N],fa[N];
    int max[N],tag[N];
    inline int find(reg int k){return k==fa[k]?k:fa[k]=find(fa[k]);}
    inline void build(reg int o){
    	max[o]=0;
    	for(reg int i=left[o];i<=right[o];i++){
    		max[o]=std::max(max[o],a[i]);
    		if(v[o][a[i]].root) fa[i]=v[o][a[i]].root;
    		else v[o][a[i]].root=i,fa[i]=i,pos[i]=a[i];
    		v[o][a[i]].num++;
    	}
    }
    inline void merge(reg int o,int s,int t){
    	Node *S=&v[o][s],*T=&v[o][t];
    	if(T->root) fa[S->root]=T->root;
    	else T->root=S->root,pos[T->root]=t;
    	T->num+=S->num;
    	S->num=S->root=0;
    }
    inline void pushdown(reg int o){
    	for(reg int i=left[o];i<=right[o];i++){
    		a[i]=pos[find(i)];
    		v[o][a[i]].root=v[o][a[i]].num=0;	
    		a[i]-=tag[o];
    	}
    	for(reg int i=left[o];i<=right[o];i++) fa[i]=0;
    	tag[o]=0;
    }
    inline void maketag(reg int o,reg int x){
    	if((x<<1)<max[o]-tag[o]){
    		for(reg int i=tag[o]+1;i<=tag[o]+x;i++)if(v[o][i].root) merge(o,i,i+x);
    		tag[o]+=x;
    	}
    	else{
    		for(reg int i=max[o];i>tag[o]+x;i--)if(v[o][i].root) merge(o,i,i-x);
    		max[o]=std::min(max[o],tag[o]+x);
    	}
    }
    int main(){
    	n=read();int m=read();
    	blocksize=std::sqrt(n);blocknum=(n-1)/blocksize+1;
    	std::memset(left,0x3f,sizeof left);
    	for(reg int i=1;i<=n;i++){
    		a[i]=read();belong[i]=(i-1)/blocksize+1;
    		left[belong[i]]=std::min(left[belong[i]],i);right[belong[i]]=std::max(right[belong[i]],i);
    	}
    	for(reg int i=1;i<=blocknum;i++) build(i);
    	int op;reg int l,r,x;while(m--){
    		op=read();l=read();r=read();x=read();
    		int L=belong[l],R=belong[r];
    		if(op==1){
    			if(L^R){
    				pushdown(L);pushdown(R);
    				for(reg int i=l;i<=right[L];i++)if(a[i]>x) a[i]-=x;
    				for(reg int i=left[R];i<=r;i++)if(a[i]>x) a[i]-=x;
    				build(L);build(R);
    				for(reg int i=L+1;i<R;i++) maketag(i,x);
    			}
    			else{
    				pushdown(L);
    				for(reg int i=l;i<=r;i++)if(a[i]>x) a[i]-=x;
    				build(L);
    			}
    		}
    		else{
    			int ans=0;
    			if(L^R){
    				for(reg int i=l;i<=right[L];i++) ans+=(pos[find(i)]-tag[L]==x);
    				for(reg int i=left[R];i<=r;i++) ans+=(pos[find(i)]-tag[R]==x);
    				for(reg int i=L+1;i<R;i++) ans+=(tag[i]+x>=N?0:v[i][tag[i]+x].num);
    			}
    			else for(reg int i=l;i<=r;i++) ans+=(pos[find(i)]-tag[L]==x);
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    SpringCloud 学习之概述
    定位慢查询
    中止线程
    笨办法41学会说面向对象【pyinstaller安装使用
    pip安装
    笨办法40模块, 类和对象class
    笨办法39字典dict
    笨办法38列表操作
    笨办法35分支和函数
    笨办法34访问列表元素(列表方法)
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/14833403.html
Copyright © 2011-2022 走看看