abc351g 题解

这场 abc F、G 质量堪忧。怎么能扔板子上来呢?

板子:P4719 【模板】"动态 DP"&动态树分治

Solution

这种每次修改对后面询问有影响,又每次都要输出答案的,离线就基本做不了了,这时候就往动态算法想,其实做过几道 ddp 的题就看出来这是个板子。

由于题目中的式子性质优良,我们很明显可以把它写成矩阵的形式。

我们预处理轻儿子的乘积和,即 \(g_{u}=\prod\limits_{v\in son,v\ne Hson}f_v\) ,然后问题中的式子就可以写成这个矩阵:

\[\begin{bmatrix} f_{son} & 1 \\ \end{bmatrix} \times \begin{bmatrix} g_{now} & 0\\ a_{now} & 1\\ \end{bmatrix}= \begin{bmatrix} f_{now} & 1 \\ \end{bmatrix}\]

然后因为不方便转移,要从后往前乘,我们改造一下就变成:

\[\begin{bmatrix} g_{now} & a_{now}\\ 0 & 1\\ \end{bmatrix} \times \begin{bmatrix} f_{son} \\ 1 \end{bmatrix}= \begin{bmatrix} f_{now} \\ 1 \end{bmatrix} \]

初始矩阵是 \(\begin{bmatrix}0 \\ 1\end{bmatrix}\)

这样就可以从根节点往下乘了。

然后之后操作修改就是模板了,撤销操作用逆元即可。

然后这题一个小麻烦的地方就来了。因为这题有 \(0\) ,然后 \(0\) 没有逆元,所以要进行一些适当的处理即可,这个地方我选择的使用一个数组记录下轻儿子 \(0\) 的个数,\(g\) 数组不乘 \(0\)

然后就是 ddp 的一些细节了。

使用的是树剖的写法,\(w=2\) 是矩阵大小,还有个求逆元,时间复杂度 \(O(q\log^2 n\times w^3+q\log n\log mod)\)

因为笔者的习惯,代码中 \(f\) 代表上述的 \(g\) 数组,代码中的 \(g\) 数组代表矩阵。

Code

#include<bits/stdc++.h>
#define N 200005
#define ll long long
using namespace std;
const int mod=998244353;
ll inv(ll a,ll x=mod-2)
{
	ll res=1;
	while(x)
	{
		if(x&1) res=res*a%mod;
		a=a*a%mod;
		x>>=1;
	}
	return res;
}
int n;
int head[N],tot=1;
int a[N];
struct edge{
	int to,next;
}e[N*2];
void add(int u,int v)
{
	e[tot]=(edge){v,head[u]};
	head[u]=tot++;
}
ll f[N],w[N];
struct node{
	int r[2][2];
	int n,m;
}g[N],oo,zero;
node operator * (node a,node b)
{
	node c;
	c.n=a.n,c.m=b.m;
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			c.r[i][j]=0;
	for(int k=0;k<2;k++)
		for(int i=0;i<2;i++)
			for(int j=0;j<2;j++)
				c.r[i][j]=(c.r[i][j]+(1ll*a.r[i][k]*b.r[k][j]%mod))%mod;
	return c;
}
int pos[N];
struct segtree{
	node tr[N*4];
	void modify(int l,int r,int p,int x)
	{
		if(l==r)
		{
			tr[x]=g[pos[p]];
			return;
		}
		int mid=(l+r)/2;
		if(p<=mid) modify(l,mid,p,x*2);
		else modify(mid+1,r,p,x*2+1);
		tr[x]=tr[x*2]*tr[x*2+1];
	} 
	node query(int l,int r,int L,int R,int x)
	{
		if(l>R||r<L) return oo;
		if(l>=L&&r<=R) return tr[x]; 
		int mid=(l+r)/2;
		node lv=query(l,mid,L,R,x*2),rv=query(mid+1,r,L,R,x*2+1);
		if(lv.n==-1) return rv;
		if(rv.n==-1) return lv;
		return lv*rv;
	}
}tr;
int fa[N],dep[N],siz[N],Son[N];
int ed[N],id[N],ids,top[N];
int tag[N];
void dfs1(int now,int f)
{
	fa[now]=f;
	dep[now]=dep[f]+1;
	siz[now]=1;
	int maxx=0;
	for(int i=head[now];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v==f) continue;
		dfs1(v,now);
		siz[now]+=siz[v];
		if(siz[v]>maxx) maxx=siz[v],Son[now]=v;
	}
}
void dfs2(int now,int topf)
{
	++ids;
	id[now]=ids;
	pos[ids]=now;
	ed[topf]=id[now];
	top[now]=topf;
	if(Son[now]) dfs2(Son[now],topf),w[now]=w[Son[now]];
	f[now]=1;
	for(int i=head[now];i;i=e[i].next)
	{
		int son=e[i].to;
		if(son==fa[now]||son==Son[now]) continue;
		dfs2(son,son);
		w[now]=w[now]*w[son]%mod;
		if(!w[son]) tag[now]++;
		else f[now]=f[now]*w[son]%mod;
	}
	w[now]+=a[now],w[now]%=mod;
	g[now].n=g[now].m=2;
	g[now].r[0][0]=f[now],g[now].r[0][1]=a[now],
	g[now].r[1][0]=0,g[now].r[1][1]=1;
	if(tag[now]) g[now].r[0][0]=0;
	tr.modify(1,n,id[now],1);
}
void updata(int x,ll v)
{
	g[x].r[0][1]=v;
	while(x)
	{
		ll last=tr.query(1,n,id[top[x]],ed[top[x]],1).r[0][1];
		tr.modify(1,n,id[x],1);
		ll now=tr.query(1,n,id[top[x]],ed[top[x]],1).r[0][1];
		x=fa[top[x]];
		if(!last) tag[x]--;
		else f[x]=f[x]*inv(last)%mod;
		if(!now) tag[x]++;
		else f[x]=f[x]*now%mod;
		if(tag[x]) g[x].r[0][0]=0;
		else g[x].r[0][0]=f[x];
	}
}
int q;
int main()
{
	oo.n=-1;
	zero.n=zero.m=2;
	zero.r[1][1]=1;
	scanf("%d%d",&n,&q);
	for(int i=2;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		add(x,i);
	}
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	dfs1(1,0);
	dfs2(1,1);
	while(q--)
	{
		int v,x;
		scanf("%d%d",&x,&v);
		updata(x,v);
		printf("%d\n",tr.query(1,n,1,ed[1],1).r[0][1]);
	}
	return 0;
}
posted @ 2024-04-28 15:08  g1ove  阅读(23)  评论(0编辑  收藏  举报