BSOJ7020题解
脑抽了。考场上应该做掉这题的。
所以实际挂分从100pts变成了200pts/fn/fn/fn
考虑用一个二元组来维护链, 表示这个集合的所有链的点权和为 ,有 条链,目的是方便转移。
定义两个二元组 的二元运算: 表示合并两个集合, 表示将两个集合中选择两条链拼接起来之后的链集合,对于常数 , 表示给所有链的权值都加上一个 , 表示答案集合。
给每个边加一个点权 (大于号和小于号没区别),于是问题就变成了边权交错。
设 表示 到子树内的链中, 的儿子节点权值是 的链集合。(用上述二元组维护)
合并答案的话,因为另一条链要被反过来,所以实际上对于点对 ( 父亲相同)实际上要求的应该是 。但是需要加上一个链 ,所以实际上是:
同理。
对于 的转移,显然就是 。
接下来考虑换根。
首先对于 ,先把所有与其连接的节点按照上述方式转移。
然后,对于一个儿子,应该将其对自身的贡献去掉。容易发现上述转移是存在逆操作的,只需要定义两个集合之间的减法即可。
然后。。。随便转移就完了。。。。。。甚至没有细节。。。。。。
#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);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】