BSOJ6444题解

神秘做法+1

考虑一个很 simple 的 DP:\(dp[u][c]\) 表示节点 \(u\) 的颜色为 \(c\) 时子树的方案数。

转移是 \(dp[u][x]=\prod_{v\in son_u}{\sum_{x\ne y}dp[v][y]}\)

\(g[u]=\sum_{i=1}^{m}dp[u][i]\) 的话,有:

\[dp[u][x]=\prod_{v\in son_u}(g[v]-dp[v][x]) \]

边界条件是叶子结点的 \(dp\) 值均为 \(1\)

考虑线段树合并。

每次合并结束后用单点修改直接删去单点的值。

然后相当于需要实现:全局取反,全局加,把两个序列做点积。

这三个操作都是简单的。

复杂度 \(O((n+k)\log m)\)

细节的话

前面两个操作相当于区间加和区间乘。

考虑怎么把对应的位置乘起来。

在线段树合并的时候,有一个节点是空的的时候,说明那一边所有的叶子结点的值一定相同。

把这个值算出来就可以区间乘了。

需要注意不能 pushdown 在合并儿子之前就需要判断。

全局和什么的东西应该很好维护的吧

#include<cstdio>
#include<cctype>
#include<vector>
typedef unsigned ui;
const ui M=1e5+5,mod=998244353;
ui n,m,k,egd,tot,h[M<<1],f[M<<1],rt[M<<1],sum[6*30*M],ls[6*30*M],rs[6*30*M];ui len,stk[6*30*M];
std::vector<ui>c[M<<1];
struct Edge{
	ui v,nx;
}e[M<<2];
inline void Add(const ui&u,const ui&v){
	e[++egd]=(Edge){v,h[u]};h[u]=egd;
	e[++egd]=(Edge){u,h[v]};h[v]=egd;
}
struct func{
	ui k,b;
	inline func(const ui&k=1,const ui&b=0):k(k),b(b){}
	inline ui operator()(const ui&x)const{
		return(1ull*k*x+b)%mod;
	}
	inline func operator()(const func&g)const{
		return func(1ull*k*g.k%mod,(1ull*k*g.b+b)%mod);
	}
}t[6*30*M];
inline ui nnd(){
	const ui u=len?stk[len--]:++tot;ls[u]=rs[u]=sum[u]=0;t[u]=func();return u;
}
inline void del(const ui&u){
	stk[++len]=u;
}
inline void push(const ui&u,const func&f,const ui&L,const ui&R){
	t[u]=f(t[u]);sum[u]=(1ull*f.k*sum[u]+1ull*f.b*(R-L+1))%mod;
}
inline void pushdown(const ui&u,const ui&L,const ui&R){
	if(!ls[u])ls[u]=nnd();if(!rs[u])rs[u]=nnd();
	const ui&mid=L+R>>1;push(ls[u],t[u],L,mid);push(rs[u],t[u],mid+1,R);t[u]=func();
}
inline void update(const ui&u,const ui&L,const ui&R){
	sum[u]=(1ull*t[u].k*(sum[ls[u]]+sum[rs[u]])+1ull*t[u].b*(R-L+1))%mod;
}
inline void Modify(ui&u,const ui&l,const ui&r,const func&F,const ui&L=1,const ui&R=m){
	if(l>R||L>r)return;if(l<=L&&R<=r)return push(u,F,L,R);
	const ui&mid=L+R>>1;pushdown(u,L,R);Modify(ls[u],l,r,F,L,mid);Modify(rs[u],l,r,F,mid+1,R);update(u,L,R);
}
inline ui merge(const ui&q,const ui&p,const ui&L=1,const ui&R=m){
	if(!q||!p)return del(q|p),q&p;
	if(!ls[q]&&!rs[q])return push(p,func(t[q].b),L,R),p;if(!ls[p]&&!rs[p])return push(q,func(t[p].b),L,R),q;
	const ui&mid=L+R>>1;pushdown(q,L,R);pushdown(p,L,R);
	ls[q]=merge(ls[q],ls[p],L,mid);rs[q]=merge(rs[q],rs[p],mid+1,R);update(q,L,R);del(p);
	return q;
}
inline void DFS(const ui&u){
	rt[u]=nnd();sum[rt[u]]=m;t[rt[u]]=func(1,1);
	for(ui v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u])f[v]=u,DFS(v),rt[u]=merge(rt[u],rt[v]);
	for(ui&x:c[u])Modify(rt[u],x,x,func(0,0));if(u^1)Modify(rt[u],1,m,func(mod-1,sum[rt[u]]));
}
inline ui read(){
	ui n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
signed main(){
	n=read();m=read();k=read();
	for(ui u,v,i=1;i<n;++i)u=read(),v=read(),Add(u,v);for(ui u,v,i=1;i<=k;++i)u=read(),v=read(),c[u].push_back(v);
	DFS(1);printf("%u",sum[rt[1]]);
}
posted @ 2022-05-14 11:04  Prean  阅读(21)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};