BSOJ7020题解

脑抽了。考场上应该做掉这题的。

所以实际挂分从100pts变成了200pts/fn/fn/fn

考虑用一个二元组来维护链,\((f,g)\) 表示这个集合的所有链的点权和为 \(f\),有 \(g\) 条链,目的是方便转移。

定义两个二元组 \(x,y\) 的二元运算:\(x+y\) 表示合并两个集合,\(x\times y\) 表示将两个集合中选择两条链拼接起来之后的链集合,对于常数 \(c\)\(x\times c\) 表示给所有链的权值都加上一个 \(c\)\(f[u]\) 表示答案集合。

给每个边加一个点权 \(w[u]=[V[u]<V[f[u]]]\)(大于号和小于号没区别),于是问题就变成了边权交错。

\(f0[u],f1[u]\) 表示 \(u\) 到子树内的链中,\(u\) 的儿子节点权值是 \(0/1\) 的链集合。(用上述二元组维护)

合并答案的话,因为另一条链要被反过来,所以实际上对于点对 \((u,v)\)\(u,v\) 父亲相同)实际上要求的应该是 \(w[u]=w[v]\)。但是需要加上一个链 \((u,v)\),所以实际上是:

\[f1[u]=f1[u]+(f0[v]\times V[v]+(V[u]+V[v],1)) \]

\(f0\) 同理。

对于 \(f\) 的转移,显然就是 \(f[u]=f[u]+f[v]+(w[v]?(f0[v]\times V[v])\times f1[u]:(f1[v]\times V[v])\times f0[u])\)

接下来考虑换根。

首先对于 \(u\),先把所有与其连接的节点按照上述方式转移。

然后,对于一个儿子,应该将其对自身的贡献去掉。容易发现上述转移是存在逆操作的,只需要定义两个集合之间的减法即可。

然后。。。随便转移就完了。。。。。。甚至没有细节。。。。。。

#include<cstdio>
#include<cctype>
typedef long long ll;
const int M=1e5+5;
int n,q,mod,ege,w[M],V[M],h[M];int f[M],d[M],son[M],siz[M],top[M];
struct data{
	int f,g;
	data(const int&f=0,const int&g=0):f(f),g(g){}
	inline data operator+(const data&it)const{
		return data((f+it.f)%mod,(g+it.g)%mod);
	}
	inline data operator-(const data&it)const{
		return data((mod+f-it.f)%mod,(mod+g-it.g)%mod);
	}
	inline data operator*(const data&it)const{
		return data((1ll*f*it.g+1ll*g*it.f)%mod,1ll*g*it.g%mod);
	}
	inline data operator+(const int&it)const{
		return data((f+it)%mod,g+1);
	}
	inline data operator*(const int&it)const{
		return data((f+1ll*g*it)%mod,g);
	}
}F0[M],F1[M],G0[M],G1[M],F[M],G[M];
struct Edge{
	int v,nx;
}e[M<<1];
inline void Add(const int&u,const int&v){
	e[++ege]=(Edge){v,h[u]};h[u]=ege;
	e[++ege]=(Edge){u,h[v]};h[v]=ege;
}
inline void DFS1(const int&u){
	siz[u]=1;d[u]=d[f[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],siz[v]>siz[son[u]]&&(son[u]=v);
}
inline void DFS2(const int&u,const int&tp){
	top[u]=tp;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 Find(int u,const int&v){
	while(top[u]^top[v]){
		if(f[top[u]]==v)return top[u];u=f[top[u]];if(!u)return-1;
	}
	return d[u]<d[v]?-1:son[v];
}
inline void DP1(const int&u){
	F0[u]=F1[u]=F[u]=data();
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u]){
		DP1(v);F[u]=F[u]+(F[v]+(w[v]?F1[u]*(F0[v]+V[v]):F0[u]*(F1[v]+V[v])));
		if(w[v])F1[u]=F1[u]+(F0[v]*V[u]+(V[u]+V[v]));else F0[u]=F0[u]+(F1[v]*V[u]+(V[u]+V[v]));
	}
	F[u]=F[u]+F0[u]+F1[u];
}
inline void DP2(const int&u){
	data f0,f1,sum;G[u]=G[u]+G0[u]+G1[u];
	for(int v,E=h[u];E;E=e[E].nx){
		if((v=e[E].v)==f[u]){
			const int&W=w[u]^1;
			sum=sum+(G[u]+(W?f1*(G0[u]+V[v]):f0*(G1[u]+V[v])));
			if(W)f1=f1+(G0[u]*V[u]+(V[u]+V[v]));else f0=f0+(G1[u]*V[u]+(V[u]+V[v]));
		}
		else{
			const int&W=w[v];
			sum=sum+(F[v]+(W?f1*(F0[v]+V[v]):f0*(F1[v]+V[v])));
			if(W)f1=f1+(F0[v]*V[u]+(V[u]+V[v]));else f0=f0+(F1[v]*V[u]+(V[u]+V[v]));
		}
	}
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u]){
		const int&W=w[v];
		if(W)f1=f1-(F0[v]*V[u]+(V[u]+V[v]));else f0=f0-(F1[v]*V[u]+(V[u]+V[v]));
		sum=sum-(F[v]+(W?f1*(F0[v]+V[v]):f0*(F1[v]+V[v])));
		G[v]=sum;G0[v]=f0;G1[v]=f1;DP2(v);
		sum=sum+(F[v]+(W?f1*(F0[v]+V[v]):f0*(F1[v]+V[v])));
		if(W)f1=f1+(F0[v]*V[u]+(V[u]+V[v]));else f0=f0+(F1[v]*V[u]+(V[u]+V[v]));
	}
}
inline int read(){
	int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline void write(int n){
	static char s[15];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);putchar(10);
}
signed main(){
	int r,s;n=read();mod=read();
	for(int i=1;i<=n;++i)V[i]=read();for(int u,v,i=1;i<n;++i)u=read(),v=read(),Add(u,v);
	DFS1(1);DFS2(1,1);for(int i=2;i<=n;++i)w[i]=V[f[i]]>V[i];DP1(1);DP2(1);q=read();
	while(q--){
		int x;r=read();s=read();x=Find(r,s);
		if(s==r)write(F[1].f);else if(!~x)write(F[s].f);else write(G[x].f);
	}
}
posted @ 2022-08-17 16:01  Prean  阅读(12)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};