zoukankan      html  css  js  c++  java
  • [bzoj 5143][Ynoi 2018]五彩斑斓的世界

    传送门

    Descroption

    给了你一个长为n的序列a,有m次操作

    1.把区间[l,r]中大于x的数减去x

    2.查询区间[l,r]中x的出现次数

    Solution

    分块

    对于每个块,我们都分别维护:

    • 每种数出现的次数,需要用到并查集,即把相同的数合并在一起

    • 标记tag,表示这个块总共减了多少

    • maxx,表示这个块的最大值

    对于查询,按照分块的套路直接查询就可以了

    修改的时候,为了保证复杂度正确,我们考虑每次通过(O(x))的复杂度,实现整个块的极差减少(x).

    这样,我们就能保证总修改的复杂度是(O(nlog n))

    怎么做到呢?

    假设当前数组的极差为(M),我们近似的将它看成是(maxx-tag)

    • (M< x) 显然是不存在大于x的数,直接返回即可
    • (x leq M leq 2x) 这个时候,把大于x的数直接减去x即可,fa[j]=fa[j-x],这样极差减少(x)
    • (M>2x) 直接把小于等于(x)的数加上(x),然后整个区间打上减(x)的标记,极差同样减少(x)

    Code 

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    char B[1<<26],*S=B;
    inline int read(){
        int x;char c;
        while((c=*S++)<'0'||c>'9');
        for(x=c-'0';(c=*S++)>='0'&&c<='9';)x=x*10+c-'0';
        return x;
    }
    const int MN=1e5,T=650;
    int a[MN+5],fa[MN/T+5][MN+5],sz[MN/T+5][MN+5],minn[T+5],maxx[T+5];
    int getf(int*f,int k){return f[k]?f[k]=getf(f,f[k]):k;}
    int main()
    {
    	B[fread(B,1,1<<26,stdin)]=0;
    	int n,m,i,j,o,l,r,x,lk,rk,ans;
    	n=read();m=read();
    	for(i=1;i<=n;++i)a[i]=read(),++sz[(i-1)/T][a[i]];
    	for(i=1;i<=n;i+=T) minn[i/T]=0,maxx[i/T]=MN;
    	while(m--)
    	{
    		o=read();l=read();r=read();x=read();
    		lk=(l-1)/T;rk=(r-1)/T;
    		if(o==1)
    		{
    			for(i=l;i<=r&&i<=lk*T+T;++i)
    				if((a[i]=getf(fa[lk],a[i]))-minn[lk]>x)--sz[lk][a[i]],++sz[lk][a[i]-=x];
    			for(i=lk;++i<rk;)
    				if(maxx[i]-minn[i]>2*x)
    				{
    					for(j=1;j<=x;++j)sz[i][fa[i][minn[i]+j]=minn[i]+j+x]+=sz[i][minn[i]+j];
    					minn[i]+=x;
    				}
    				else
    				{
    					for(j=x+1;j<=maxx[i]-minn[i];++j)sz[i][fa[i][minn[i]+j]=minn[i]+j-x]+=sz[i][minn[i]+j];
    					maxx[i]=min(maxx[i],minn[i]+x); 
    				}
    			if(lk!=rk)for(i=r;i>rk*T;--i)
    				if((a[i]=getf(fa[rk],a[i]))-minn[rk]>x)--sz[rk][a[i]],++sz[rk][a[i]-=x];
    		}
    		else
    		{
    			ans=0;
    			for(i=l;i<=r&&i<=lk*T+T;++i)if(getf(fa[lk],a[i])-minn[lk]==x)++ans;
    			for(i=lk;++i<rk;)if(x+minn[i]<=maxx[i])ans+=sz[i][x+minn[i]];
    			if(lk!=rk)for(i=r;i>rk*T;--i)if(getf(fa[rk],a[i])-minn[rk]==x)++ans;
    			printf("%d
    ",ans);
    		}
    	}
    }
    


    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    JS中数组去除重复的方法
    ember.js里的实用方法
    Ember入门指南——教程目录
    如何解决问题?
    Web前端开发工程师基本要求
    (转)轻松学习JavaScript三:JavaScript与HTML的结合
    (转)JavaScript二:JavaScript语言的基本语法要求
    HTML的checkbox和radio的美化
    C#串口通信—向串口发送数据,同步接收返回数据
    C#生成验证码
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/10182708.html
Copyright © 2011-2022 走看看