专栏文章

题解:P9597 [JOI Open 2018] 猫或狗 / Cats or Dogs

P9597题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mindmzbs
此快照首次捕获于
2025/12/02 00:42
3 个月前
此快照最后确认于
2025/12/02 00:42
3 个月前
查看原文
fi,(0,1)f_{i,(0,1)} 表示第 ii 个点的子树,不存在有猫或狗的节点于其相连,所要删除的最少边数。先写出朴素 DP 的矩阵转移方程:
fu=[fv,0fv,1]×[fu,0fu,1+1fu,0+1fu,1] f_u= \begin{bmatrix} f_{v,0} & f_{v,1} \end{bmatrix} \times \begin{bmatrix} f'_{u,0} & f'_{u,1}+1 \\ f'_{u,0}+1 & f'_{u,1} \end{bmatrix}
特别的,若 uu 位置上有猫或狗,再乘上一个 mask 矩阵。
思考如何快速处理好修改。我们先重链剖分一下,每个点记录其转移过所有轻儿子后的,不转移重儿子的矩阵 g=[f0f1+1f0+1f1]g = \begin{bmatrix} f'_{0} & f'_{1}+1 \\ f'_{0}+1 & f'_{1} \end{bmatrix}。于是我们用线段树维护每条重链,一个点的 ff 就是矩阵 [00]\begin{bmatrix} 0 & 0 \end{bmatrix} 乘重链末尾到该点的所有 gg。修改只会改到根节点路径上的重链 top 的父节点的 gg 矩阵,故时间复杂度两个 log\log
Code:
CPP
/*

		2025.11.4

  * Happy Zenith Noises *

*/
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005,inf=0x3f3f3f3f,mod=998244353;
struct matrix{
	int n,m,a[2][2];
	void init(){n=m=2,a[0][0]=a[1][1]=0,a[0][1]=a[1][0]=inf;}
	const matrix operator *(const matrix y)const{
		matrix ans;ans.n=n,ans.m=y.m;memset(ans.a,0x3f,sizeof(ans.a));
		for(int i=0;i<n;i++)for(int j=0;j<y.m;j++)for(int k=0;k<m;k++)ans.a[i][j]=min(ans.a[i][j],a[i][k]+y.a[k][j]);
		return ans;
	}
}f[MAXN],tmp,gg[MAXN],t[3];
struct node{int l,r;matrix w,mask,ww;}tree[MAXN*4];
int n,a[MAXN];
int dfn[MAXN],rev[MAXN],top[MAXN],ed[MAXN],cnt;
int son[MAXN],si[MAXN],dep[MAXN],mx[MAXN],fa[MAXN];
vector<int>g[MAXN];
void pre_dfs(int x){
	si[x]=1;
	for(int i:g[x]){
		if(si[i])continue;
		pre_dfs(i);si[x]+=si[i];fa[i]=x;
		if(si[i]>si[son[x]])son[x]=i;
	}
}
void dfs(int x,int top){
	dfn[x]=++cnt;rev[cnt]=x;::top[x]=top;
	if(son[x])dfs(son[x],top);
	for(int i:g[x])if(!dfn[i]&&i!=son[x])dfs(i,i);
}
void build(int x,int l,int r){
	tree[x].l=l,tree[x].r=r;
	if(l==r){
		tree[x].w.n=tree[x].w.m=2;
		tree[x].w.a[0][1]=tree[x].w.a[1][0]=1;tree[x].w.a[0][0]=tree[x].w.a[1][1]=0;
		tree[x].mask=tree[x].w,tree[x].ww=tree[x].w*tree[x].mask;
		return ;
	}
	int mid=(l+r)>>1;
	build(x*2,l,mid);build(x*2+1,mid+1,r);
	tree[x].ww=tree[x*2+1].ww*tree[x*2].ww;
}
matrix ask(int x,int l,int r,int op){
	if(l>r){matrix ans;ans.init();return ans;}
	if(tree[x].l>=l&&tree[x].r<=r)return (op?tree[x].w:tree[x].ww);
	int mid=(tree[x].l+tree[x].r)>>1;
	matrix ans;ans.init();
	if(l<=mid)ans=ask(x*2,l,r,op);
	if(r>mid)ans=ask(x*2+1,l,r,op)*ans;
	return ans;
}
void add(int x,int l,const matrix &t,const matrix &t2){
	if(tree[x].l==tree[x].r){
		tree[x].w=t,tree[x].mask=t2,tree[x].ww=tree[x].w*tree[x].mask;
		return ;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	if(l<=mid)add(x*2,l,t,t2);
	else add(x*2+1,l,t,t2);
	tree[x].ww=tree[x*2+1].ww*tree[x*2].ww;
}
int st(int x,int t){
	matrix now=ask(1,dfn[x],dfn[x],1);
	add(1,dfn[x],now,::t[t]);a[x]=t;
	int tp=top[x];
	while(tp!=1){
		matrix tf=tmp*ask(1,dfn[tp],ed[tp],0);
		now=ask(1,dfn[fa[tp]],dfn[fa[tp]],1);
		int f0=now.a[0][0],f1=now.a[1][1];
		f0-=min(f[tp].a[0][0],f[tp].a[0][1]+1),f1-=min(f[tp].a[0][1],f[tp].a[0][0]+1);
		f0+=min(tf.a[0][0],tf.a[0][1]+1),f1+=min(tf.a[0][1],tf.a[0][0]+1);
		f[tp]=tf;
		now.a[0][0]=f0,now.a[1][0]=f0+1,now.a[0][1]=f1+1,now.a[1][1]=f1;
		add(1,dfn[fa[tp]],now,::t[a[fa[tp]]]);
		x=fa[tp],tp=top[x];
	}
	now=tmp*ask(1,1,ed[1],0);int ttt=min(now.a[0][0],now.a[0][1]);
	return ttt;
}
int cat(int v){return st(v,0);}
int dog(int v){return st(v,1);}
int neighbor(int v){return st(v,2);}
void initialize(int N,vector<int>A,vector<int>B){
	n=N;
	t[2].n=t[2].m=2;t[2].a[0][0]=t[2].a[1][1]=0,t[2].a[1][0]=t[2].a[0][1]=inf;
	t[0]=t[2],t[1]=t[2];
	t[0].a[0][0]=inf,t[0].a[1][0]=inf,t[1].a[1][1]=inf;t[1].a[1][0]=inf;
	tmp.n=1,tmp.m=2;tmp.a[0][0]=tmp.a[0][1]=0;
	for(int i=0;i<n-1;i++)g[A[i]].push_back(B[i]),g[B[i]].push_back(A[i]);
	for(int i=1;i<=n;i++)a[i]=2,f[i].n=1,f[i].m=2,f[i].a[0][0]=f[i].a[0][1]=0;
	pre_dfs(1);dfs(1,1);
	for(int i=1;i<=n;i++)mx[top[i]]=max(mx[top[i]],dfn[i]);
	for(int i=1;i<=n;i++)ed[i]=mx[top[i]];
	build(1,1,n);
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...