ShinriiTin's Blog - 博主是sb

听说下雨天,分块和莫队更配哦~

恩。。。先跪PoPoQQQ

 

bzoj3585 mex

题目大意:  询问区间mex值

这题有用线段树的O(nlogn)做法,以前发过了,所以这里不做讨论

既然是区间询问且支持离线的数据结构题,我们很容易想到用莫队算法。

那么问题就在于区间之间如何转移。

想法1: 用平衡树维护插入和删除,询问时在平衡树上二分。

这样做每次插入、删除和询问都是O(logn)的

由于莫队算法需要进行O(q*sqrt(n))次插入或删除,则总的复杂度会达到O(q*sqrt(n)*logn).

这个做法的瓶颈在于插入和删除时的花费太大。

想法2: 将权值分块,维护每种权值出现的次数和每块有多少种权值出现。

这样可以做到每次O(1)的插入和删除,插入和删除的总

询问时先暴力找到第一个存在未存在的数的块,再在这个块内暴力,复杂度O(sqrt(n))。

虽然询问的复杂度高于上一种做法,但是实际上询问只会有q次,总的复杂度依然是O(n*sqrt(n)).

Portal

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

#define g() getchar()
template<class Q>void Scan(Q&x){
	char c;int f=1;
	while(c=g(),c<48||c>57)if(c=='-')f=-1;
	for(x=0;c>47&&c<58;c=g())x=10*x+c-48;
	x*=f;
}

#define rep(i,a,b) for(int i=a;i<b;++i)

const int MAXN=2e5+5;

int n,m,B,tot;

int a[MAXN],pos[MAXN];

struct qy{
	int l,r,id;
}q[MAXN];
inline bool cmp(const qy&a,const qy&b){
	if(pos[a.l]!=pos[b.l])return a.l<b.l;
	return pos[a.l]&1?a.r<b.r:a.r>b.r;
}

inline void Init(){
	Scan(n),Scan(m);
	B=sqrt(n)+1;
	tot=n/B+1;
	rep(i,1,n+1){
		Scan(a[i]);
		if(a[i]>n)a[i]=n;
	}
	rep(i,0,n+1)pos[i]=i/B+1;
	rep(i,1,m+1){
		Scan(q[i].l),Scan(q[i].r);
		q[i].id=i;
	} 
}

int cnt[MAXN],cnt1[MAXN]; 

inline void Add(int x){
	if(++cnt[x]==1)++cnt1[pos[x]];
}
inline void Del(int x){
	if(!(--cnt[x]))--cnt1[pos[x]];
}
inline int query(){
	int i=1;
	for(;i<=tot&&cnt1[i]==B;++i); 
	for(i=(i-1)*B;cnt[i]&&i<n;++i);
	return i; 
}

int ans[MAXN];

inline void Solve(){
	sort(q+1,q+1+m,cmp);
	int l=q[1].l,r=q[1].r;
	rep(i,l,r+1)Add(a[i]);
	ans[q[1].id]=query();
	rep(i,2,m+1){
		int ll=q[i].l,rr=q[i].r;
		for(;r<rr;Add(a[++r]));
		for(;r>rr;Del(a[r--]));
		for(;l>ll;Add(a[--l]));
		for(;l<ll;Del(a[l++]));
		ans[q[i].id]=query();
	}
	rep(i,1,m+1)
		printf("%d\n",ans[i]);
}

int main(){
	Init(); Solve(); return 0;
}

 

bzoj4129 Haruna's Breakfast

题目大意: 询问树上两点间路径的mex值,支持单点修改

这题是上一题的加强版,从线形推广到树形。

这题如果从线段树入手会十分困难(其实是博主太弱了不会这样做,有会这种做法的请联系我)

考虑到莫队算法是可以推广到树上的,我们直接用上一题的思路就好了。

由于带修改,对树分块时块的大小最好设为n2/3,对权值分块还是sqrt(n)就好。

简单说下树上路径的转移方法,为了方便,以下所说的路径均不含lca。

假设现在的路径是(x1,y1),需要转移到路径(x2,y2).

我们将路径(x1,x2),(y1,y2)取反,就得到了(x2,y2).

Portal

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

#define g() getchar()
template<class Q>void Scan(Q&x){
    char c; while(c=g(),c<48||c>57);
    for(x=0;c>47&&c<58;c=g())x=10*x+c-48;
}

#define rep(i,a,b) for(int i=a;i<b;++i)

const int MAXN=50005;

int B1,Bn,a[MAXN],pos[MAXN];

struct edge{
    int t,n;
    edge(int t=0,int n=0):t(t),n(n){}
}e[MAXN<<1];
int head[MAXN],tot;
inline void AddEdge(int s,int t){
    e[++tot]=edge(t,head[s]),head[s]=tot;
    e[++tot]=edge(s,head[t]),head[t]=tot;
}

int n,m,B2,bel[MAXN],siz[MAXN];

struct qy{
    int x,y,z,id;
    qy(int x=0,int y=0,int z=0,int id=0):
		x(x),y(y),z(z),id(id){} 
}q[MAXN];

inline bool cmp(const qy&a,const qy&b){
    if(bel[a.x]!=bel[b.x])return bel[a.x]<bel[b.x];
    if(bel[a.y]!=bel[b.y])
		return bel[a.x]&1?bel[a.y]<bel[b.y]:bel[a.y]>bel[b.y];
    return bel[a.y]&1?a.z<b.z:a.z>b.z;
}

struct tarjan{
    int x,id,n;
    tarjan(int x=0,int id=0,int n=0):x(x),id(id),n(n){}
}t[MAXN<<1];
int thd[MAXN],qtot;
inline void AddTarjan(int x,int y,int id){
    t[++qtot]=tarjan(y,id,thd[x]),thd[x]=qtot;
    t[++qtot]=tarjan(x,id,thd[y]),thd[y]=qtot;
}

bool col[MAXN];

int f[MAXN];
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}

int p[MAXN],dep[MAXN],lca[MAXN];

void dfs(int x){
    for(int i=head[x],y;i;i=e[i].n)
        if((y=e[i].t)!=p[x]){
            if(siz[bel[x]]<B2){
                ++siz[bel[y]=bel[x]];
            }
            p[y]=x;
            dep[y]=dep[x]+1;
            dfs(y);
        }
    col[x]=1;
    for(int i=thd[x],y;i;i=t[i].n)
        if(col[y=t[i].x])lca[t[i].id]=find(y);
    f[x]=p[x];
}

bool on[MAXN];

int cnt[MAXN],cnt1[MAXN];

inline void Add(int x){
    if(++cnt[x]==1)++cnt1[pos[x]];
}
inline void Del(int x){
    if(--cnt[x]==0)--cnt1[pos[x]];
}
inline int query(){
	int i=1;
	for(;i<Bn&&cnt1[i]==B1;++i);
	for(i=(i-1)*B1;i<n&&cnt[i];++i);
	return i;
}

inline void Xor(int x){
    on[x]^=1;
    if(on[x])Add(a[x]);
    else Del(a[x]);
}

struct modify{
    int x,f,t;
    modify(int x=0,int f=0,int t=0):
    	x(x),f(f),t(t){}
}mo[MAXN];

int mod_tot,qy_tot;

inline void change(int x,int y){
    if(on[x])Del(a[x]);
    a[x]=y;
    if(on[x])Add(a[x]);
}
inline void Modify(int cur,int to){
    for(;cur<to;++cur)change(mo[cur+1].x,mo[cur+1].t);
    for(;cur>to;--cur)change(mo[cur].x,mo[cur].f);
}

inline void Rev(int x,int y){
    if(dep[x]>dep[y])swap(x,y);
    while(dep[y]>dep[x])Xor(y),y=p[y];
    while(x!=y)Xor(x),Xor(y),x=p[x],y=p[y];
}

int ans[MAXN];

inline void Init(){
    Scan(n),Scan(m);
    B1=sqrt(n),Bn=n/B1+1;
    B2=pow(n,2./3);
    pos[0]=1;
    rep(i,1,n+1){
    	pos[i]=i/B1+1;
        Scan(a[i]);
        if(a[i]>n)a[i]=n;
    	f[i]=bel[i]=i;   
    }
    rep(i,1,n){
        int x,y;
        Scan(x),Scan(y);
        AddEdge(x,y);
    }
    rep(i,1,m+1){
        int t,x,y;
        Scan(t),Scan(x),Scan(y);
        if(t){
			++qy_tot;
			q[qy_tot]=qy(x,y,mod_tot,qy_tot);
			AddTarjan(x,y,qy_tot);
        }
		else{
			if(y>n)y=n;
			mo[++mod_tot]=modify(x,a[x],y);
			a[x]=y;
		}
    }
    dfs(1);
    rep(i,1,qy_tot+1){
		if(bel[q[i].x]>bel[q[i].y])
			swap(q[i].x,q[i].y); 
	} 
}

inline void Solve(){
	sort(q+1,q+1+qy_tot,cmp);
	int x=q[1].x,y=q[1].y,z=q[1].z;
	int Lca=lca[q[1].id];
	Modify(mod_tot,z);
	Rev(x,y);
	Xor(Lca);
	ans[q[1].id]=query();
	Xor(Lca);
	rep(i,2,qy_tot+1){
		Modify(z,q[i].z),z=q[i].z;
		Rev(x,q[i].x),x=q[i].x;
		Rev(y,q[i].y),y=q[i].y;
		Lca=lca[q[i].id];
		Xor(Lca);
		ans[q[i].id]=query();
		Xor(Lca);
	}
	rep(i,1,qy_tot+1){
		printf("%d\n",ans[i]);
	} 
}

int main(){
	Init(); Solve(); return 0;
}