悬赏两个关注
查看原帖
悬赏两个关注
728090
RaynorLzj楼主2025/2/1 23:54
// Problem: P4689 [Ynoi2016] 这是我自己的发明
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4689
// Memory Limit: 512 MB
// Time Limit: 1500 ms
// Author: RaynorLzj
// 
// Powered by CP Editor (https://cpeditor.org)
//
// PS: 莫队是数据结构???
// 莫队拆分高级版例题

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define pb push_back
#define pii pair<ll,ll>
#define fi first
#define se second
#define il inline
#define fastio ios::sync_with_stdio(false), cin.tie(0)

const int N = 1e5+5;
const int M = 5e5+5;
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
il ll read()
{
    ll x=0,f=1; char ch=nc();
    while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=nc(); }
    while(ch>=48&&ch<=57) x=x*10+ch-48,ch=nc();
   	return x*f;
}
ll head[N],tlt,n,m,nn,nnn,bel[N],len,fa[N][25],dep[N],a[N],dfn[N],rnk[N],siz[N],b[N],num[N],ans[M],T;
ll DFN_CLOCK,L=0,R=0;
ll res, cnt[3][N];  // 1: 左侧,2:右侧
struct Edge{
	ll to,nxt;
} E[2*N];

il void addedge(ll u, ll v){
	E[++tlt].to = v;
	E[tlt].nxt = head[u];
	head[u] = tlt; return ;
}

struct Ask{
	ll l,r,idx;
} q[10*M];

il bool CMP(Ask &ask1, Ask &ask2){
	if(bel[ask1.l] == bel[ask2.l]) return ask1.r < ask2.r;
	return ask1.l < ask2.l;
}

il void predfs(ll u, ll pre){ 
	dep[u] = dep[pre]+1, fa[u][0] = pre; dfn[u] = ++DFN_CLOCK; rnk[DFN_CLOCK] = u;
	siz[u]=1;
	for(int i=1; i<=24; i++) fa[u][i] = fa[fa[u][i-1]][i-1];
	for(int i=head[u]; i; i=E[i].nxt){
		if(E[i].to == pre) continue;
		predfs(E[i].to, u);
		siz[u] += siz[E[i].to];
	} return ;
}

il ll getKthfa(ll u, ll k){
	for(int i=24; i>=0; i--) if((k>>i)&1) u = fa[u][i];
	return u;
}

il void init(){
	len = sqrt(n);
	for(int i=1; i<=n; i++) bel[i]=(i+len-1)/len;
	for(int i=1; i<=nn; i++) if(q[i].l > q[i].r) swap(q[i].l, q[i].r);  // 处理区间包含的情况
	sort(q+1, q+nn+1, CMP);
	// 还要对ai离散化
	sort(b+1, b+n+1);
	nnn = unique(b+1, b+n+1) - b - 1;
	for(int i=1; i<=n; i++) num[dfn[i]] = lower_bound(b+1, b+nnn+1, a[i]) - b;
	
	// test
	return ;
}

il void AddSplit(pii segl, pii segr, ll idx){
	// ans = (pre[r1]-pre[l1])*(pre[r2]-pre[l2]) = p[r1]p[r2]-p[l1]p[r2]-p[r1]p[l2]+p[l1]p[l2] 
	q[++nn] = (Ask){segl.se, segr.se, idx};
	q[++nn] = (Ask){segl.fi-1, segr.se, -idx};
	q[++nn] = (Ask){segl.se, segr.fi-1, -idx};
	q[++nn] = (Ask){segl.fi-1, segr.fi-1, idx};
	return ;
}

il void addask(ll u, ll v, ll rt, ll idx){
	vector<pii> lques,rques;  // ans = Σcount(lques[i]) * Σcount(rques[i])
	// 这个地方要分类讨论rt在u子树内,u的子树为整棵树 - 以getkthfa(rt,dep[rt]-dep[u]-1)为根的子树
	// rt==u,u的子树就是整棵树
	// rt在u的子树外,正常
	// 叠加询问
	// u
	// printf("ask u=%lld v=%lld\n", u, v);
	ll tu=getKthfa(rt,abs(dep[rt]-dep[u])), tv=getKthfa(v,abs(dep[rt]-dep[v]));
	if(u==rt) lques.pb({1,n});//printf("u is rt\n");
	else if(tu==u){
		// printf("rt in u\n");
		ll uson=getKthfa(rt,abs(dep[rt]-dep[u]-1));
		if(dfn[uson]-1>=1) lques.pb({1,dfn[uson]-1});
		if(dfn[uson]+siz[uson]<=n) lques.pb({dfn[uson]+siz[uson], n});
	}
	else lques.pb({dfn[u], dfn[u]+siz[u]-1});//printf("normal u\n");
	// v
	if(v==rt) rques.pb({1,n});//printf("v is rt\n");
	else if(tv==v){
		// printf("rt in v\n");
		ll vson=getKthfa(rt,abs(dep[rt]-dep[v]-1));
		if(dfn[vson]-1>=1) rques.pb({1,dfn[vson]-1});
		if(dfn[vson]+siz[vson]<=n) rques.pb({dfn[vson]+siz[vson], n});
	}
	else rques.pb({dfn[v], dfn[v]+siz[v]-1});//printf("normal v\n");
	// 拆分询问(乘法分配律)
	for(pii segl : lques) for(pii segr : rques) AddSplit(segl, segr, idx);//printf("[%lld,%lld]*[%lld,%lld]\n",segl.fi,segl.se,segr.fi,segr.se);
	return ;
}

il void Add(ll val, ll pot){
	res -= cnt[1][val]*cnt[2][val];
	cnt[pot][val]++;
	res += cnt[1][val]*cnt[2][val];
	return ;
}

il void Del(ll val, ll pot){
	res -= cnt[1][val]*cnt[2][val];
	cnt[pot][val]--;
	res += cnt[1][val]*cnt[2][val];
	return ;
}

signed main(){
	fastio;
	n=read(),m=read();
	for(int i=1; i<=n; i++) a[i]=read(),b[i]=a[i];
	for(int i=2,u,v; i<=n; i++){
		u=read(),v=read();
		addedge(u,v), addedge(v,u);
	}
	predfs(1,0);
	for(int i=1,rt=1,opt,u,v; i<=m; i++){
		opt=read();
		if(opt==1) rt = read();
		else if(opt==2){
			u=read(),v=read();
			addask(u, v, rt, ++T);
		}
	}
	init();
	for(int i=1; i<=nn; i++){
		while(L<q[i].l) Add(num[++L], 1);
		while(L>q[i].l) Del(num[L--], 1);
		while(R<q[i].r) Add(num[++R], 2);
		while(R>q[i].r) Del(num[R--], 2);
		ans[abs(q[i].idx)] += res * (q[i].idx / abs(q[i].idx));
		// printf("pre[%lld]*pre[%lld] = change ans[%lld] %lld\n", q[i].l, q[i].r, abs(q[i].idx), res * (q[i].idx / abs(q[i].idx)));
	}
	for(int i=1; i<=T; i++) printf("%lld\n", ans[i]);
	return 0;
}

2025/2/1 23:54
加载中...