luogu P4689 [Ynoi2016] 这是我自己的发明
题面传送门
首先我们来看一道弱化版P5268 [SNOI2017]一个简单的询问
这个东西显然一遍差分然后莫队即可。
具体的,对于每个询问\(F(x,y,l,r)\)差分成\(F(1,y,1,r)-F(1,y,1,l-1)-F(1,x-1,1,r)+F(1,x-1,1,l-1)\)
然后就可以随便做了。
这道题是树上问题所以我们把dfs序跑出来。一个子树对应连续一段区间。
然后来看这个换根操作发现他是假的,只要看看根节点是不是在当前点子树内,不是就是原来的子树区间,是的话就是全序列刨掉根节点所在当前点子树的区间。
然后这个东西你发现最劣情况大概要拆\(9\)次询问。
考虑这个东西怎么优化。你会发现\(9\)次询问中有\(5\)次是有一项为\(n\)那么直接预处理即可。
然后就是每个四次询问了。时间复杂度\(O(n\sqrt m)\),代码奇短,真是一道小清新数据结构题。
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 100000
#define M 500000
#define mod 1000000007
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,k,root=1,op[M+5],x,Fx,Fy,y,bg[N+5],dfn[N+5],siz[N+5],dh,A[N+5],B[N+5],cnt,FA[N+5],FB[N+5],F[N+5],l,r;ll tot,Ti[N+5],Ans[M+5];map<int,int> Gs;
I void Make(int &x){!Gs[x]&&(Gs[x]=++cnt);x=Gs[x];}
struct yyy{int to,z;};set<int> Id[N+5];set<int>::it now;
struct ljb{int head,h[N+5];yyy f[N+5<<1];I void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}}s;
I void dfs(int x,int last){
yyy tmp;bg[x]=++dh;dfn[dh]=x;B[dh]=A[x];siz[x]=1;
for(int i=s.h[x];i;i=tmp.z) tmp=s.f[i],tmp.to^last&&(dfs(tmp.to,x),Id[x].insert(bg[tmp.to]),siz[x]+=siz[tmp.to]);
}
struct ques{int l,r,id,flag;}Q[M+5<<2],tmp;I bool cmp(ques x,ques y){return x.l/k==y.l/k?((x.l/k)&1?x.r<y.r:x.r>y.r):x.l<y.l;}
I void MakeQuery(int x,int y,int i,int flag){
Q[++cnt]=(ques){bg[x]+siz[x]-1,bg[y]+siz[y]-1,i,flag};
x^1&&(Q[++cnt]=(ques){bg[x]-1,bg[y]+siz[y]-1,i,-flag},0);y^1&&(Q[++cnt]=(ques){bg[x]+siz[x]-1,bg[y]-1,i,-flag},0);
x^1&&y^1&&(Q[++cnt]=(ques){bg[x]-1,bg[y]-1,i,flag},0);
}
int main(){
freopen("1.in","r",stdin);
re int i;scanf("%d%d",&n,&m);for(i=1;i<=n;i++) scanf("%d",&A[i]),Make(A[i]);cnt=0;for(i=1;i<n;i++) scanf("%d%d",&x,&y),s.add(x,y),s.add(y,x);
dfs(1,0);for(i=1;i<=n;i++) F[B[i]]++;for(i=1;i<=n;i++) Ti[i]=Ti[i-1]+F[B[i]];for(i=1;i<=m;i++){
scanf("%d",&op[i]);if(op[i]==1) scanf("%d",&root);
else{
scanf("%d%d",&x,&y);Fx=(bg[x]<bg[root]&&bg[root]<bg[x]+siz[x]);Fy=(bg[y]<bg[root]&&bg[root]<bg[y]+siz[y]);
Fy&&(swap(x,y),swap(Fx,Fy),0);if(x==root) x=1;if(y==root) y=1;
if(Fx)now=Id[x].upper_bound(bg[root]),x=dfn[*(--now)];if(Fy) now=Id[y].upper_bound(bg[root]),y=dfn[*(--now)];
if(Fx){
if(Fy) Ans[i]=Ti[n]+Ti[bg[x]-1]+Ti[bg[y]-1]-Ti[bg[x]+siz[x]-1]-Ti[bg[y]+siz[y]-1],MakeQuery(x,y,i,1);
else Ans[i]=Ti[bg[y]+siz[y]-1]-Ti[bg[y]-1],MakeQuery(x,y,i,-1);
}
else MakeQuery(x,y,i,1);
}
}k=ceil(n/sqrt(cnt));sort(Q+1,Q+cnt+1,cmp);for(i=1;i<=cnt;i++){//printf("1\n");
tmp=Q[i];while(l<tmp.l) tot+=(FB[B[++l]]),FA[B[l]]++;while(l>tmp.l) FA[B[l]]--,tot-=FB[B[l--]];
while(r<tmp.r) tot+=FA[B[++r]],FB[B[r]]++;while(r>tmp.r) FB[B[r]]--,tot-=FA[B[r--]];Ans[tmp.id]+=tot*tmp.flag;
}for(i=1;i<=m;i++) (op[i]==2)&&(printf("%d\n",Ans[i]));
}