LGP5327题解

考虑树上差分。加入一个语言或删除一个语言。先对整棵树树剖,问题变为区间加和询问全局 \(\geq 1\) 的个数。

考虑直接用线段树维护区间最小值和最小值的数量。

这个可以直接线段树合并维护。注意到总共有 \(O(n\log n)\) 次线段树修改。

然后。。。直接线段树合并就可以做到 \(O(n\log^2n)\) 了。

#include<cstdio>
#include<vector>
const int M=1e5+5;
int n,m,dfc,cnt,tot,h[M],sz[M],rt[M];long long sum;
int f[M],d[M],dfn[M],son[M],siz[M],top[M];
int tp,stk[M*18];
struct mdf{
	int L,R,V;
};std::vector<mdf>p[M];
struct Edge{
	int v,nx;
}e[M<<1];
struct Node{
	int L,R;int mi,cnt;int tag;
}t[M*18];
inline void Add(const int&u,const int&v){
	e[++cnt]=(Edge){v,h[u]};h[u]=cnt;
	e[++cnt]=(Edge){u,h[v]};h[v]=cnt;
}
inline void Erase(const int&u){
	t[u].L=t[u].R=t[u].mi=t[u].cnt=t[u].tag=0;stk[++tp]=u;
}
inline int t42d(const int&mi,const int&cnt){
	const int u=tp?stk[tp--]:++tot;t[u].L=t[u].R=t[u].tag=0;t[u].mi=mi;t[u].cnt=cnt;return u;
}
inline void update(const int&u,const int&L=1,const int&R=n){
	const int&mid=L+R>>1;int Lmi(0),Lcnt(mid-L+1),Rmi(0),Rcnt(R-mid);
	if(t[u].L)Lmi=t[t[u].L].mi,Lcnt=t[t[u].L].cnt;Lmi+=t[u].tag;
	if(t[u].R)Rmi=t[t[u].R].mi,Rcnt=t[t[u].R].cnt;Rmi+=t[u].tag;
	if(Lmi==Rmi)t[u].mi=Lmi,t[u].cnt=Lcnt+Rcnt;
	else if(Lmi>Rmi)t[u].mi=Rmi,t[u].cnt=Rcnt;
	else t[u].mi=Lmi,t[u].cnt=Lcnt;
}
inline void Mdf(int&u,const int&l,const int&r,const int&V,const int&L=1,const int&R=n){
	if(l>R||L>r)return;if(!u)u=t42d(0,R-L+1);
	if(l<=L&&R<=r)return t[u].mi+=V,t[u].tag+=V,void();
	const int&mid=L+R>>1;Mdf(t[u].L,l,r,V,L,mid);Mdf(t[u].R,l,r,V,mid+1,R);update(u,L,R);
}
inline void Merge(int&q,const int&p,const int&L=1,const int&R=n){
	if(!q||!p)return q|=p,void();t[q].tag+=t[p].tag;
	if(!t[q].L&&!t[q].R&&!t[p].L&&!t[p].R)return t[q].mi+=t[p].mi,Erase(p);
	const int&mid=L+R>>1;Merge(t[q].L,t[p].L,L,mid);Merge(t[q].R,t[p].R,mid+1,R);update(q,L,R);Erase(p);
}
inline void DFS1(const int&u){
	d[u]=d[f[u]]+1;siz[u]=1;
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u]){
		f[v]=u;DFS1(v);siz[u]+=siz[v];if(siz[v]>siz[son[u]])son[u]=v;
	}
}
inline void DFS2(const int&u,const int&tp){
	top[u]=tp;dfn[u]=++dfc;if(!son[u])return;DFS2(son[u],tp);
	for(int E=h[u];E;E=e[E].nx)if(e[E].v^f[u]&&e[E].v^son[u])DFS2(e[E].v,e[E].v);
}
inline int LCA(int u,int v){
	while(top[u]^top[v])d[top[u]]>d[top[v]]?u=f[top[u]]:v=f[top[v]];return d[u]>d[v]?v:u;
}
inline void Solve(const int&u){
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u])Solve(v),Merge(rt[u],rt[v]),sz[u]+=sz[v];
	for(mdf&it:p[u])Mdf(rt[u],it.L,it.R,it.V),sz[u]+=it.V;std::vector<mdf>().swap(p[u]);
	if(sz[u])sum+=n-(!t[rt[u]].mi?t[rt[u]].cnt:0)-1;
}
signed main(){
	scanf("%d%d",&n,&m);
	for(int u,v,i=1;i<n;++i)scanf("%d%d",&u,&v),Add(u,v);DFS1(1);DFS2(1,1);
	while(m--){
		int u,v,c,x,y;scanf("%d%d",&u,&v);c=LCA(u,v);x=u;y=v;
		while(top[x]^top[y]){
			if(d[top[x]]>d[top[y]]){
				p[u].push_back((mdf){dfn[top[x]],dfn[x],1});p[v].push_back((mdf){dfn[top[x]],dfn[x],1});
				p[f[c]].push_back((mdf){dfn[top[x]],dfn[x],-2});x=f[top[x]];
			}
			else{
				p[u].push_back((mdf){dfn[top[y]],dfn[y],1});p[v].push_back((mdf){dfn[top[y]],dfn[y],1});
				p[f[c]].push_back((mdf){dfn[top[y]],dfn[y],-2});y=f[top[y]];
			}
		}
		if(d[x]>d[y])x^=y^=x^=y;
		p[u].push_back((mdf){dfn[x],dfn[y],1});p[v].push_back((mdf){dfn[x],dfn[y],1});
		p[f[c]].push_back((mdf){dfn[x],dfn[y],-2});
	}
	Solve(1);printf("%lld",sum>>1);
}
posted @ 2022-03-25 15:37  Prean  阅读(23)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};