[Ynoi2018]五彩斑斓的世界

Posted Tan_tan_tann

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Ynoi2018]五彩斑斓的世界相关的知识,希望对你有一定的参考价值。

五彩斑斓的世界

题解

这道题相当于是Welcome home, Chtholly的数据加强版 ,对,就是那个臭名昭著的瑟尼欧里斯树的那场比赛的最后一道题
于是 l x l lxl lxl亲切地将这道题命名为第二分块,其评分好像是大分块系列中第二低的 ,但你说实话,我认为这比未来日记 那种可以一条线想下来得题难些吧

回到题目上,看到题目名题面很容易联想到分块,有条件 a i + x ⩽ 1 0 5 + 1 a_{i}+x\\leqslant 10^5+1 ai+x105+1,我们可以在值域上做点文章。
首先,如果我们认为一个块的最小值为 1 1 1,那么它的值域是不会扩大,只会减小的,因为只有大的数会变小。
我们可以将它减小的过程看做两段的合并,设最大值为 k k k:

但如果我们直接暴力合并的话明显是不行的,如果我们每次的 x x x都为 1 1 1,那么我们相当于每次都把一个很长的串放到一个很小的串上。
容易发现,我们合并前的最大值为 k k k,那么合并后最大值会变为 m a x ( x , k − x ) max\\left(x,k-x\\right) max(x,kx),我们不妨采用启发式合并的方式,使得每次我们合并的串长都与 k k k的减小量相等。
如果 2 x ⩽ k 2x\\leqslant k 2xk,我们就把 [ 1 , x ] [1,x] [1,x]的部分全部加上 x x x,再通过懒标记实现整体减 x x x,否则我们直接暴力将 [ x + 1 , k ] [x+1,k] [x+1,k]的部分减去 x x x
该过程可以通过并查集来维护,我们将 i i i连到 i − x i-x ix的根上就表示 i i i的值将变成 i − x i-x ix,很明显,这只会在 i i i还没变成其他值,即 i i i还是自己树上的根时会执行。
我们将 a i a_{i} ai变成点 a i a_{i} ai的祖先节点的值就可以实现整体操作的下传了,这样就可以避免我们暴力维护每个点值启发式合并的 l o g   n log\\,n logn
事实上这样做连路径压缩的 α ( n ) \\alpha(n) α(n)都是可以避免掉的,每次我们的连接操作都是根根的相连,而下传的过程我们也是对一个值保存下它的祖先节点是谁,其实是与我们连接次数相关的,我们的路径压缩次数与我们最开始连边的次数是一样的,均摊也是 O ( 1 ) O\\left(1\\right) O(1)
所以这块的复杂度其实只与我们块长相关,设值域为 M M M,这一块的复杂度是 O ( n S M ) O\\left(\\frac{n}{S}M\\right) O(SnM)

但由于我们对于散块的修改涉及到块长,所以我们照样不能全部都赋为一个块。
每次下传全局标记的时候我们相当于对块内的每个点都找一下它并查集上的根,由于路径压缩,所以我们对于每个值可被查找一次后都肯定已经连在根上了,而我们路径压缩部分的复杂度与我们上面解释的一样,是与之前连接的操作次数相关的,所以它的复杂度也是均摊 O ( 1 ) O\\left(1\\right) O(1)的。
所以它只与的块长相关,毕竟你块内的每个数都得下传一次。
而一个操作最多涉及 2 2 2个散块,所以下传的总次数依旧是 m m m级别的。
我们下传后的更改同样不可能让最大值变大,依旧不会影响我们之前的结论,我们只需要在更改完这个块后将其重新维护一下即可。
但一个更改前不是根的节点,在更改后如果序列中出现了这个数,它依旧可以变成根,所以我们需要将更改后块的每个数都赋为根。

查询的方式其实与修改的方式没什么区别。
只是因为查询的是出现次数,所以我们的并查集就势必要带权,维护枚棵树内的序列帧出现了的数的个数。

我们很容易发现,每个块的操作其实都是独立的。
而这个**的出题人又要卡空间, 所以我们只得将每个块单独操作,一个数组重复利用。

时间复杂度 O ( m S + ( M + m ) n S ) ⩾ ( m n ( M + m ) ) ≈ O ( n n ) O\\left(mS+(M+m)\\frac{n}{S}\\right)\\geqslant (\\sqrt{mn(M+m)})\\approx O\\left(n\\sqrt{n}\\right) O(mS+(M+m)Sn)(mn(M+m) )O(nn )
很规范的时间复杂度呀。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 1000005
#define MAXM 500005
#define MAXT 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=1e9;
const int inv2=499122177;
const int jzm=2333;
const int lim=100001;
const int n1=1000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
const int M=MAXN/n1+5;
int n,m,a[MAXN],L[M],R[M],block[MAXN],fa[MAXT],siz[MAXT],maxx,lzy,ans[MAXM];
struct ming{int opt,l,r,x;}s[MAXM];
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i,siz[i]=0;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void SetBlock(int x){
	makeSet(lim);maxx=lzy=0;
	for(int i=L[x];i<=R[x];i++)maxx=max(maxx,a[i]),siz[a[i]]++;
} 
void sakura(int x){
	if(lzy+2*x<=maxx){
		for(int i=lzy+1;i<=lzy+x;i++)
			if(fa[i]==i)fa[i]=i+x,siz[i+x]+=siz[i],siz[i]=0;
		lzy+=x;
	}
	else{
		for(int i=lzy+x+1;i<=maxx;i++)
			if(fa[i]==i)fa[i]=i-x,siz[i-x]+=siz[i],siz[i]=0;
		maxx=min(maxx,lzy+x);		
	}
}
void WorkBlock(int x){for(int i=L[x];i<=R[x];i++)a[i]=findSet(a[i]);}
signed main(){
	read(n);read(m);
	for(int i=1;i<=n;i++)read(a[i]),block[i]=(i+n1-1)/n1;
	for(int i=1;i<=m;i++)read(s[i].opt),read(s[i].l),read(s[i].r),read(s[i].x);
	for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}
	for(int i=1;i<=block[n];i

以上是关于[Ynoi2018]五彩斑斓的世界的主要内容,如果未能解决你的问题,请参考以下文章

[Ynoi2018]五彩斑斓的世界

[bzoj 5143][Ynoi 2018]五彩斑斓的世界

[Ynoi2018]未来日记

❤️ 五彩斑斓的「 算法 」世界需要多「 思考 」 ❤️(建议收藏)

❤️ 五彩斑斓的「 算法 」世界需要多「 思考 」 ❤️(建议收藏)

[Ynoi2015]此时此刻的光辉