• 【洛谷4117】[Ynoi2018] 五彩斑斓的世界(第二分块)


    点此看题面

    • 给定一个长度为(n)的序列,要求支持两种操作:将区间内大于(x)的数减去(x);询问区间中(x)的出现次数。
    • (nle10^6,qle5 imes10^5,Vle10^5)

    第二分块

    突刺贯穿的第二分块

    首先观测这道题极度卡内存,肯定无法对于每个块分别开一个(O(V))的数组。

    但是,这道题中不同的位置之间实际上并没有任何影响,因此我们有一个奇妙的想法,就是离线,枚举每个块去处理每个操作。

    考虑记录每个块的最大值(Mx),对于当前的修改值(x),分两类讨论:

    • 如果(x>frac{Mx}2):直接把所有大于(x)的数减去(x)
    • 如果(xlefrac{Mx}2):把所有小于等于(x)的数加上(x),然后打上一个全局减(x)的标记。

    由于每次这样操作之后最大值只会越来越小,一个块的总复杂度应该是(O(V))的。

    至于如何有效实现这里的加减法,显然不可能枚举每个数,而是应该枚举每种数(如果一种很大的数有许多个,显然复杂度还是会挂)。

    如果当前块是散块,直接暴力重构并查集。

    如果当前块是整块,就枚举需要修改的那些数在并查集上合并。为此需要记录每种颜色的根节点。

    询问时散块直接暴力,整块直接询问连通块大小即可。

    于是就结束了。

    代码:(O(qsqrt n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000000
    #define M 500000
    #define V 100001
    #define BT 1000
    #define BS 1000
    using namespace std;
    int n,sz,a[N+5],ans[M+5];struct Q {int op,l,r,v;}q[M+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    struct Block
    {
    	int n,fg,Mx,a[BS+5],lnk[V+5];I int& operator [] (CI x) {return a[x];}
    	int f[BS+5],g[BS+5];I int fa(CI x) {return f[x]?f[x]=fa(f[x]):x;}
    	I void Build() {Mx=0;for(RI i=1;i<=n;++i) lnk[a[i]]?++g[f[i]=lnk[a[i]]]:g[lnk[a[i]]=i]=1,Mx=max(Mx,a[i]);}//初始化,建并查集
    	I void Cl() {for(RI i=1;i<=n;++i) lnk[a[i]=a[fa(i)]]=0,f[i]=0;for(RI i=1;i<=n;++i) a[i]-=fg;fg=0;}//清空一切,清除标记
    	I void Union(CI x,CI y) {lnk[y]&&(lnk[x]?g[f[lnk[y]]=lnk[x]]+=g[lnk[y]]:a[lnk[x]=lnk[y]]=x,lnk[y]=0);}//并查集上合并
    	I int Q(CI v) {return fg+v<=V&&lnk[fg+v]?g[lnk[fg+v]]:0;}//询问连通块大小
    	I int G(CI l,CI r,CI v) {RI t=0;for(RI i=l;i<=r;++i) a[fa(i)]==fg+v&&++t;return t;}//暴力询问
    	I void U(CI v)//整块修改
    	{
    		if(2*v>Mx-fg) {for(RI i=fg+v+1;i<=Mx;++i) Union(i-v,i);Mx=min(Mx,fg+v);}//如果v>Mx/2
    		else {for(RI i=fg+v;i>=fg+1;--i) Union(i+v,i);fg+=v;}//如果v≤Mx/2
    	}
    	I void BF(CI l,CI r,CI v)//暴力修改
    	{
    		RI i;for(Cl(),i=l;i<=r;++i) a[i]>v&&(a[i]-=v);for(fg=Mx=0,i=1;i<=n;++i) Mx=max(Mx,a[i]);Build();//修改前清空标记,修改后重构
    	}
    }B;
    int main()
    {
    	RI Qt,i,j;for(read(n,Qt),i=1;i<=n;++i) read(a[i]);for(i=1;i<=Qt;++i) read(q[i].op,q[i].l,q[i].r,q[i].v);
    	RI L,R,op,l,r,v;for(sz=sqrt(n),i=1;i<=(n-1)/sz+1;++i)
    	{
    		for(L=(i-1)*sz+1,R=min(i*sz,n),B.Cl(),B.n=0,j=L;j<=R;++j) B[++B.n]=a[j];//把元素加到块中
    		for(B.Build(),j=1;j<=Qt;++j) L<=q[j].r&&R>=q[j].l&&
    		(
    			op=q[j].op,l=max(q[j].l-L+1,1),r=min(q[j].r-L+1,B.n),v=q[j].v,
    			l==1&&r==B.n?(op==1?(B.U(v),0):ans[j]+=B.Q(v)):(op==1?(B.BF(l,r,v),0):ans[j]+=B.G(l,r,v))//整块/散块
    		);
    	}
    	for(i=1;i<=Qt;++i) q[i].op==2&&(writeln(ans[i]),0);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    BUUCTF | [极客大挑战 2019]PHP
    BUUCTF | [极客大挑战 2019]Secret File
    [网鼎杯 2018] Fakebook 复现
    [强网杯2019 随便注]总结
    [Flask(Jinja2)服务端模板注入漏洞(SSTI)]学习简记
    [Flask框架]学习简记
    ADO.NET基础
    SQL Server基础
    AdventureWorks2012下载链接
    SQL Server2012从入门到精通
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4117.html
Copyright © 2020-2023  润新知