【bzoj4940】这是我自己的发明

Portal --> bzoj4940

Solution

  (原题这题面到底是。。怎么回事啊深深的套路qwq)

  感觉自己对根号的算法还是很。。没有感觉啊==

  实际上这题和bzoj5016没有任何区别的感觉。。。那个换根操作不过是一个幌子而已,处理出\(dfn\)序之后根据当前根\(rt\)\(dfn\)序和\(x\)的子树范围的关系可以直接将查询变成\(dfn\)序上的至多两段区间

  然后就变成了和bzoj5016一模一样的东西。。只是区间可能有两个这样的话直接两两组合一下什么的就好了

​  具体的话还是写一下:其实我们需要处理的是这样一个式子

\[\sum\limits_{x}cnt_x(l_1,r_1)\cdot cnt_x(l_2,r_2) \]

  然后我们来快乐处理式子

\[\begin{aligned} &\sum\limits_{x}cnt_x(l_1,r_1)\cdot cnt_x(l_2,r_2)\\ =&\sum\limits_{x}(cnt_x(1,r_1)-cnt_x(1,l_1-1))\cdot (cnt_x(1,r_2)-cnt_x(1,l_2-1))\\ =&\sum\limits_{x}cnt_x(1,r_1)\cdot cnt_x(1,r_2)-\sum\limits_{x}cnt_x(1,r_1)\cdot cnt_x(1,l_2-1)-\sum\limits_{x}cnt_x(1,l_1-1)\cdot cnt_x(1,r_2)+\sum\limits_{x}cnt_x(1,l_1-1)\cdot cnt_x(1,l_2-1) \end{aligned} \]

  然后我们记\(Q(l,r)=\sum\limits_{x}cnt_x(1,l)\cdot cnt_x(1,r)\),那么也就是说我们要求的是

\[\sum\limits_{x}Q(r_1,r_2)-Q(r_1,l_2-1)-Q(l_1-1,r_2)+Q(l_1-1,l_2-1) \]

  然后对于\(Q(l,r)\)我们可以用莫队处理

  对于每一个值(离散化之后就至多只有\(n\)个了)记一个\(cnt\)表示当前区间中的数量,具体一点的话就是假设当前的左右指针分别为\(l\)\(r\)\(cntl[x]\)记录\([1,l]\)区间内\(x\)这个值得数量,\(cntr[x]\)记录\([1,r]\)区间内\(x\)这个值的数量,然后再用\(nowval\)记录一下当前的答案,每次移动的时候加上(或者减去)对应的贡献就好了(具体的话其实还是加上\(cntl[val[r]]\)或者\(cntr[val[l]]\),反正就是。。上面乘法中的其中一个\(-1\)了然后拆一下括号什么的就很清楚了)

  那么这样的话,对于原问题中的每组询问我们应该会得到。。至多。。\(9\)\(Q(l,r)\)这样的询问,所以直接莫队爆搞一波就好了

  复杂度的话。。emmm虽然说算出来很大但是。。可以过嗯qwq

  

​  mark:场上真的。。实在不行就想一想根号的做法吧qwq并不是说什么题都要log的啊对不对qwq根号说不定就过了呢==

  
  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define Pr pair<int,int>
#define mp make_pair
using namespace std;
const int N=100010,M=500010,TOP=20;
struct xxx{
	int y,nxt;
}a[N*2];
int num[N];
struct Q{
	int l,r,id,val;
	Q(){}
	Q(int l1,int r1,int id1,int val1){l=l1; r=r1; id=id1; val=val1;}
	friend bool operator < (Q x,Q y){return num[x.l]==num[y.l]?x.r<y.r:num[x.l]<num[y.l];}
}q[M*10];
Pr rec1[10],rec2[10];
int f[N][TOP+1],dep[N],mxdfn[N];
int h[N],val[N],lis[N],st[N],ed[N];
ll ans[M],cntl[N],cntr[N];
int n,m,sq,cntq,dfn_t,rt,tot,cntQ;
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void solve(){
	sort(q+1,q+1+cntq);
	int nowl=0,nowr=0;
	ll nowval=0;
	for (int i=1;i<=cntq;++i){
		while (nowl<q[i].l)
			nowval+=cntr[val[lis[++nowl]]],++cntl[val[lis[nowl]]];
		while (nowr<q[i].r)
			nowval+=cntl[val[lis[++nowr]]],++cntr[val[lis[nowr]]];
		while (nowl>q[i].l)
			nowval-=cntr[val[lis[nowl]]],--cntl[val[lis[nowl--]]];
		while (nowr>q[i].r)
			nowval-=cntl[val[lis[nowr]]],--cntr[val[lis[nowr--]]];
		ans[q[i].id]+=nowval*q[i].val;
	}
}
void prework(){
	lis[0]=n;
	sort(lis+1,lis+1+lis[0]);
	lis[0]=unique(lis+1,lis+1+lis[0])-lis-1;
	for (int i=1;i<=n;++i)
		val[i]=lower_bound(lis+1,lis+1+lis[0],val[i])-lis;
}
void dfs(int fa,int x,int d){
	int u;
	st[x]=++dfn_t; lis[dfn_t]=x; dep[x]=d; mxdfn[x]=dfn_t;
	f[x][0]=fa;
	for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa) continue;
		dfs(x,u,d+1);
		mxdfn[x]=max(mxdfn[x],mxdfn[u]);
	}
	ed[x]=dfn_t;
}
int jump(int x,int d){
	if (!d) return x;
	for (int i=0;i<=TOP;++i)
		if (d>>i&1)
			x=f[x][i];
	return x;
}
void get_seg(int x,Pr *rec,int &cnt){
	int pre;
	cnt=0;
	if (rt==x){
		rec[++cnt]=mp(1,n);
	}
	else if (st[rt]<st[x]||st[rt]>ed[x]){
		rec[++cnt]=mp(st[x],ed[x]);
	}
	else{
		pre=jump(rt,dep[rt]-dep[x]-1);
		if (st[pre]>1) rec[++cnt]=mp(1,st[pre]-1);
		if (mxdfn[pre]<n) rec[++cnt]=mp(mxdfn[pre]+1,n);
	}
}
void add_q(int l1,int r1,int l2,int r2,int id){
	q[++cntq]=Q(r1,r2,id,1);
	if (l2-1)
		q[++cntq]=Q(r1,l2-1,id,-1);
	if (l1-1)
		q[++cntq]=Q(l1-1,r2,id,-1);
	if (l2-1&&l1-1)
		q[++cntq]=Q(l1-1,l2-1,id,1);
}
void div(int x,int y,int id){
	int cnt1,cnt2,l1,r1,l2,r2;
	get_seg(x,rec1,cnt1);
	get_seg(y,rec2,cnt2);
	for (int i=1;i<=cnt1;++i)
		for (int j=1;j<=cnt2;++j)
			add_q(rec1[i].first,rec1[i].second,rec2[j].first,rec2[j].second,id);
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,y,op;
	scanf("%d%d",&n,&m);
	sq=sqrt(n);
	for (int i=1;i<=n;++i) scanf("%d",val+i),num[i]=(i-1)/sq,lis[i]=val[i];
	prework();
	memset(h,-1,sizeof(h));
	tot=0;
	for (int i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfn_t=0; lis[0]=0;
	dfs(0,1,1);
	rt=1;
	cntq=0; cntQ=0;
	for (int i=1;i<=m;++i){
		scanf("%d",&op);
		if (op==1){
			scanf("%d",&rt);
		}
		else{
			scanf("%d%d",&x,&y);
			div(x,y,++cntQ);
		}
	}
	solve();
	for (int i=1;i<=cntQ;++i) printf("%lld\n",ans[i]);
}
posted @ 2018-10-16 18:56  yoyoball  阅读(273)  评论(4编辑  收藏  举报