[YNOI 2016]bzoj 4940 这是我自己的发明 - 莫队

题目大意:
给一棵树点有颜色,多次询问(x,y)求从x子树中选一个点,再从y子树中选一个点,二者颜色相同,有多少种情况;或者支持换根。可离线, n ≤ 1 0 5 , m ≤ 5 × 1 0 5 n\le10^5,m\le5\times 10^5 n105,m5×105
题解:
首先通过dfs序将问题转化为序列上的问题,然后(l1,r1),(l2,r2)可以拆成若干(1,r1’),(1,r2’)的问题这样用莫队做(r1’,r2’)的维护即可。
关于换根相当于是全集扣掉了一个子区间,一个维护的好办法是把序列倍长,这样还是一个区间,方便处理。
一些常数优化形如莫队的时候奇数块正直搞偶数块倒着搞之类的……

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define lint long long
#define pb push_back
using namespace std;const int N=100010,M=500010,LOG=19;
namespace IO{
	const int S=(1<<20)+5;char buf[S],*H,*T;char obuf[S],*oS=obuf,*oT=oS+S-1,c,qu[55];int qr;
	inline char Get() { if(H==T) T=(H=buf)+fread(buf,1,S,stdin);if(H==T) return -1;return *H++; }
	inline int inn() { int x=0;char c=Get();while(!isdigit(c)) c=Get();while(isdigit(c)) x=x*10+c-'0',c=Get();return x; }
	inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;} inline void putc(char x){*oS++ =x;if(oS==oT) flush();}
	template <class I>inline void show(I x) { if(!x) putc('0');while(x) qu[++qr]=(char)(x%10+'0'),x/=10;while(qr) putc(qu[qr--]);putc('\n'); }
}using namespace IO;struct edges{int to,pre;}e[N<<1];int h[N],etop,Log[N],up[N][LOG],d[N],val[N<<1],a[N],L[N],R[N],bel[N<<1],dfc;
vector<int> v;inline int getid(int x) { return (int)(lower_bound(v.begin(),v.end(),x)-v.begin()+1); }
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
inline int gety(int y,int x) { for(int i=Log[d[y]];i>=0;i--) if(d[up[y][i]]>d[x]) y=up[y][i];return y; }
int dfs(int x,int fa=0)
{
	val[L[x]=++dfc]=a[x]=getid(a[x]),d[x]=d[up[x][0]=fa]+1;rep(i,1,Log[d[x]]) up[x][i]=up[up[x][i-1]][i-1];
	for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa) dfs(y,x);return R[x]=dfc;
}
inline int getLR(int x,int rt,int &s,int &t,int n)
{ if(L[x]<=L[rt]&&L[rt]<=R[x]) { if(x==rt) return s=1,t=n;int y;return y=gety(rt,x),s=R[y]+1,t=L[y]-1+n; } return s=L[x],t=R[x]; }
struct Q{ int l,r,id,sgn;Q(int _l=0,int _r=0,int _sgn=0,int _id=0) { l=_l,r=_r,id=_id,sgn=_sgn; }
	inline bool operator<(const Q &q)const { if(bel[l]^bel[q.l]) return bel[l]<bel[q.l];if(bel[l]&1) return r<q.r;return r>q.r; }
}q[M<<2];lint ans[M],curAns;int cnt[2][N];
inline int add(int x,int g) { if(!x) return 0;return curAns+=cnt[g^1][val[x]],cnt[g][val[x]]++; }
inline int del(int x,int g) { if(!x) return 0;return curAns-=cnt[g^1][val[x]],cnt[g][val[x]]--; }
int main()
{
	int n=inn(),m=inn(),x,y,qcnt=0;rep(i,1,n) a[i]=inn(),v.pb(a[i]);sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end());
	rep(i,2,n) x=inn(),y=inn(),add_edge(x,y),add_edge(y,x),Log[i]=Log[i>>1]+1;
	int rt=1,qc=0,sz=(int)sqrt(1.25*n);rep(i,1,2*n) bel[i]=(i-1)/sz+1;dfs(rt);rep(i,1,n) val[i+n]=val[i];
	while(m--)
		if(inn()==1) rt=inn();
		else{
			int x=inn(),y=inn(),Lx,Rx,Ly,Ry;getLR(x,rt,Lx,Rx,n),getLR(y,rt,Ly,Ry,n);
			qcnt++,q[++qc]=Q(Rx,Ry,1,qcnt);if(Lx>1&&Ly>1) q[++qc]=Q(Lx-1,Ly-1,1,qcnt);
			if(Lx>1) q[++qc]=Q(Lx-1,Ry,-1,qcnt);if(Ly>1) q[++qc]=Q(Ly-1,Rx,-1,qcnt);
		}
	rep(i,1,qc) if(q[i].l>q[i].r) swap(q[i].l,q[i].r);sort(q+1,q+qc+1);int L=0,R=0;curAns=0ll;
	for(int i=1;i<=qc;ans[q[i].id]+=q[i].sgn*curAns,i++)
	{ while(L<q[i].l) add(++L,0);while(R<q[i].r) add(++R,1);while(L>q[i].l) del(L--,0);while(R>q[i].r) del(R--,1); }
	rep(i,1,qcnt) show(ans[i]);return flush(),0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值