ddp

刷树剖题单时发现剩下的模版,顺便补了。

P4719 【模板】动态 DP

给定一棵树,点有权值,每次单点修改,求全局最大独立集。

\(n \leq 10^6,q \leq 3 \times 10^6\)

静态做法显然,设 \(f_{i,0/1}\) 表示以 \(i\) 为根的子树内 \(i\) 是否选择的最大独立集,显然有转移:

\[f_{i,1} = a_i+\sum_{j \in son_i} f_{i,0},f_{i,0} = \sum_{j\in son_i} \max(f_{i,0},f_{i,1}) \]

不妨考虑这个东西怎么做修改,为了迎合重链剖分的做法,我们设 \(g_{i,0}\) 表示 \(i\) 的轻儿子可取可不取的最大独立集,\(g_{i,1}\) 表示 \(i\) 的轻儿子全不取的最大独立集。

这种东西的思想或许跟 dsu on tree 类似,通过重剖的性质来均摊复杂度,改进之后我们有方程,令 \(j\)\(i\) 的重儿子:

\[f_{i,1} = a_i + g_{i,1} + f_{j,0} ,f_{i,0} = \max(f_{j,0},f_{j,1}) + g_{i,0} \]

不妨将 \(g_{i,1}\)\(a_i\) 合并,将其意义改为轻儿子全不取当前点必选的最大独立集,这样转移方程就与 \(a_i\) 无关。

关于 \(f_i\) 的区间维护,我们考虑类似做斐波那契时的想法,利用矩阵

在斐波那契中,我们使用的是普通矩阵乘法,但在这里,涉及了 \(\max\) 运算,我们不如重新定义一下矩阵乘法,

新定义 \(\ast\) 运算符,\(A \ast B\) 的结果 \(C\),满足 \(C_{i,j} = \max_k(A_{i,k}+B_{k,j})\),当然这个东西是满足结合律的,感性理解就是因为加法和 \(\max\) 运算都满足结合律。

好的,我们此时发明了广义矩阵乘法,类似矩阵快速幂的来构造关系矩阵,令关系矩阵为 \(U\)

\[\begin{bmatrix}f_{j,0} & f_{j,1}\end{bmatrix} \ast U = \begin{bmatrix}f_{i,0}&f_{i,1}\end{bmatrix} \]

简单手模一下就能得到,转移应为:

\[\begin{bmatrix}f_{j,0} & f_{j,1}\end{bmatrix} \ast \begin{bmatrix}g_{i,0}&g_{i,1}\\g_{i,0} & -\infty\end{bmatrix} = \begin{bmatrix}f_{i,0}&f_{i,1}\end{bmatrix} \]

好了,到这里我们就已经发明了 ddp,维护时,这玩意好像不是很满足交换律,所以线段树维护时是从左到右维护,把转移转过来就好了:

\[\begin{bmatrix}g_{i,0}&g_{i,0}\\g_{i,1} & -\infty\end{bmatrix} \ast \begin{bmatrix}f_{j,0} \\ f_{j,1}\end{bmatrix}= \begin{bmatrix}f_{i,0} \\ f_{i,1}\end{bmatrix} \]

因为我们的定义很特殊,所以对于一个重链的链头的答案显然是整条重链的矩阵乘积,因此我们还需要每条重链的开头结尾。

修改时,因为每到链头向上跳的时候就不是重儿子了,所以链头父亲的关系矩阵需要改变,将原贡献减掉再加上新贡献就好了,好了,做完。

复杂度 \(O(2^3 n\log^2 n)\)

加强版其实双 log 也可以草过去的,只不过需要卡一卡常,比方说用前向星存图,改成对每条重链开动态开点线段树,能省去几个查询时的 log,优化还是比较明显的。

正常版:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar('\n')
#define fi first
#define se second
#define pp putchar(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#define pis pair<ll,char >
//#pragma GCC optimize(2)
using namespace std;
typedef int ll;
//typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar('-');x=-x;}if(x>9){wr(x/10);}putchar(x%10+'0');}
const ll N=3e5+5,M=5e6,mod=998244353;
ll n,a[N],son[N],top[N],id[N],w[N],siz[N],dep[N],fa[N],f[N][2],End[N],cnt,m;
vector<ll> G[N];
struct Mat{
	ll mat[2][2];
	Mat(){mem(mat,-0x3f);}
	inline Mat operator * (Mat b){Mat c;fo(0,i,1) fo(0,j,1) fo(0,k,1) c.mat[i][j]=max(c.mat[i][j],mat[i][k]+b.mat[k][j]);return c;}
}g[N];
struct SGT{ll l,r;Mat sum;}tree[N<<2];
#define rt tree[root]
#define ls tree[root<<1]
#define rs tree[root<<1|1]
inline void pushup(ll root){rt.sum=ls.sum*rs.sum;}
inline void build(ll root,ll l,ll r){rt.l=l,rt.r=r;if(l==r) return rt.sum=g[w[l]],void();ll mid=l+r>>1;build(root<<1,l,mid),build(root<<1|1,mid+1,r);pushup(root);}
inline void upd(ll root,ll x){ll l=rt.l,r=rt.r;if(l==r) return rt.sum=g[w[x]],void();ll mid=l+r>>1;if(x<=mid) upd(root<<1,x);else upd(root<<1|1,x);pushup(root);}
inline Mat ask(ll root,ll x,ll y){ll l=rt.l,r=rt.r;if(x<=l&&y>=r) return rt.sum;ll mid=l+r>>1;if(x>mid) return ask(root<<1|1,x,y);if(y<=mid) return ask(root<<1,x,y);return ask(root<<1,x,y)*ask(root<<1|1,x,y);}
inline void dfs1(ll x,ll fat,ll depth){fa[x]=fat,siz[x]=1,dep[x]=depth;for(ll y:G[x]){if(y==fa[x]) continue;dfs1(y,x,depth+1);siz[x]+=siz[y];if(siz[y]>siz[son[x]]) son[x]=y;}}
inline void dfs2(ll x,ll nowtop)
{
	top[x]=nowtop;id[x]=++cnt;w[cnt]=x;End[nowtop]=max(End[nowtop],cnt);
	f[x][0]=0,f[x][1]=a[x];
	g[x].mat[0][0]=g[x].mat[0][1]=0;g[x].mat[1][0]=a[x];
	if(son[x]) dfs2(son[x],nowtop),f[x][1]+=f[son[x]][0],f[x][0]+=max(f[son[x]][1],f[son[x]][0]); 
	for(ll y:G[x])
	{
		if(y==fa[x]||y==son[x]) continue;
		dfs2(y,y);f[x][1]+=f[y][0];
		f[x][0]+=max(f[y][1],f[y][0]);
		g[x].mat[0][0]+=max(f[y][0],f[y][1]);
		g[x].mat[0][1]=g[x].mat[0][0];
		g[x].mat[1][0]+=f[y][0];
	}
}
inline void upd_chain(ll x,ll w)
{
	g[x].mat[1][0]+=w-a[x];a[x]=w;
	Mat a,b;
	while(x)
	{
		a=ask(1,id[top[x]],End[top[x]]);upd(1,id[x]);b=ask(1,id[top[x]],End[top[x]]);x=fa[top[x]];
		g[x].mat[0][0]+=max(b.mat[0][0],b.mat[1][0])-max(a.mat[0][0],a.mat[1][0]);g[x].mat[0][1]=g[x].mat[0][0];
		g[x].mat[1][0]+=b.mat[0][0]-a.mat[0][0];
	}
}
signed main(){
	read(n);read(m);fo(1,i,n) read(a[i]);fo(1,i,n-1){ll u,v;read(u),read(v);G[u].pb(v),G[v].pb(u);}dfs1(1,0,1),dfs2(1,1);build(1,1,n);
	fo(1,i,m)
	{
		ll x,y;read(x),read(y);
		upd_chain(x,y);Mat ans=ask(1,id[1],End[1]);
		wr(max(ans.mat[0][0],ans.mat[1][0])),pr;
	}
	return 0;
}

卡常版,通过了加强版,注意 _unlocked 在本地不过编。

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define inf 0x3f3f3f3f
#define pr putchar_unlocked('\n')
#define fi first
#define se second
#define pp putchar_unlocked(' ')
#define pii pair<ll,ll>
#define pdi pair<ll,ll>
#define mem(aa,bb) memset(aa,bb,sizeof(aa))
#define fo(a,i,b) for(register ll i = a ; i <= b ; ++ i )
#define Fo(a,i,b) for(register ll i = a ; i >= b ; -- i )
#define pb push_back
//#define pis pair<ll,char >
//#pragma GCC optimize(2)
using namespace std;
typedef int ll;
//typedef long long ll;
//typedef __int128 ll;
typedef double db;
inline void read(ll &opp){ll x=0,t=1;char ch;ch=getchar_unlocked();while(ch<'0'||ch>'9'){if(ch=='-'){t=-1;}ch=getchar_unlocked();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar_unlocked();}opp=x*t;return; }
inline void wr(ll x){if(x<0){putchar_unlocked('-');x=-x;}if(x>9){wr(x/10);}putchar_unlocked(x%10+'0');}
const ll N=1e6+5,M=5e6,mod=998244353;
ll n,a[N],son[N],top[N],id[N],w[N],siz[N],dep[N],fa[N],f[N][2],End[N],cnt,m,head[N],tot,rot[N],opt;
struct Edge{ll nxt,ver;}G[N<<1];
inline void add(ll u,ll v){++tot;G[tot].ver=v,G[tot].nxt=head[u];head[u]=tot;}
struct Mat{
	ll mat[2][2];
	Mat(){mem(mat,-0x3f);}
	inline Mat operator * (Mat b)
	{
		Mat c;
		c.mat[0][0]=max(mat[0][0]+b.mat[0][0],mat[0][1]+b.mat[1][0]);
		c.mat[1][0]=max(mat[1][0]+b.mat[0][0],mat[1][1]+b.mat[1][0]);
		c.mat[0][1]=max(mat[0][0]+b.mat[0][1],mat[0][1]+b.mat[1][1]);
		c.mat[1][1]=max(mat[1][0]+b.mat[0][1],mat[1][1]+b.mat[1][1]);
		return c;
	}
}g[N];
struct SGT{ll l,r;Mat sum;}tree[N<<2];
#define rt tree[root]
#define ls tree[tree[root].l]
#define rs tree[tree[root].r]
inline void pushup(ll root){rt.sum=ls.sum*rs.sum;}
inline void build(ll &root,ll l,ll r){if(!root) root=++opt;if(l==r) return rt.sum=g[w[l]],void();ll mid=l+r>>1;build(rt.l,l,mid),build(rt.r,mid+1,r);pushup(root);}
inline void upd(ll root,ll x,ll l,ll r){if(l==r) return rt.sum=g[w[x]],void();ll mid=l+r>>1;if(x<=mid) upd(rt.l,x,l,mid);else upd(rt.r,x,mid+1,r);pushup(root);}
inline Mat ask(ll root,ll x,ll y,ll l,ll r){if(x<=l&&y>=r) return rt.sum;ll mid=l+r>>1;if(x>mid) return ask(rt.r,x,y,mid+1,r);if(y<=mid) return ask(rt.l,x,y,l,mid);return ask(rt.l,x,y,l,mid)*ask(rt.r,x,y,mid+1,r);}
inline void dfs1(ll x,ll fat,ll depth){fa[x]=fat,siz[x]=1,dep[x]=depth;for(ll i=head[x];i;i=G[i].nxt){ll y=G[i].ver;if(y==fa[x]) continue;dfs1(y,x,depth+1);siz[x]+=siz[y];if(siz[y]>siz[son[x]]) son[x]=y;}}
inline void dfs2(ll x,ll nowtop)
{
	top[x]=nowtop;id[x]=++cnt;w[cnt]=x;End[nowtop]=max(End[nowtop],cnt);
	f[x][0]=0,f[x][1]=a[x];
	g[x].mat[0][0]=g[x].mat[0][1]=0;g[x].mat[1][0]=a[x];
	if(son[x])
	{
		dfs2(son[x],nowtop);
		f[x][1]+=f[son[x]][0];
		f[x][0]+=max(f[son[x]][1],f[son[x]][0]); 
	}
	for(ll i=head[x];i;i=G[i].nxt)
	{
		ll y=G[i].ver;
		if(y==fa[x]||y==son[x]) continue;
		dfs2(y,y);
		f[x][1]+=f[y][0];
		f[x][0]+=max(f[y][1],f[y][0]);
		g[x].mat[0][0]+=max(f[y][0],f[y][1]);
		g[x].mat[0][1]=g[x].mat[0][0];
		g[x].mat[1][0]+=f[y][0];
	}
}
inline void upd_chain(ll x,ll w)
{
	g[x].mat[1][0]+=w-a[x];a[x]=w;
	Mat a,b;
	while(x)
	{
		a=tree[rot[top[x]]].sum;upd(rot[top[x]],id[x],id[top[x]],End[top[x]]);b=tree[rot[top[x]]].sum;x=fa[top[x]];
		g[x].mat[0][0]+=max(b.mat[0][0],b.mat[1][0])-max(a.mat[0][0],a.mat[1][0]);
		g[x].mat[0][1]=g[x].mat[0][0];
		g[x].mat[1][0]+=b.mat[0][0]-a.mat[0][0];
	}
}
signed main(){
	read(n);read(m);fo(1,i,n) read(a[i]);fo(1,i,n-1){ll u,v;read(u),read(v);add(u,v),add(v,u);}
	dfs1(1,0,1),dfs2(1,1);fo(1,i,n) if(top[i]==i) build(rot[i],id[i],End[i]);
	ll lastans=0;
	fo(1,i,m)
	{
		ll x,y;read(x),read(y);x^=lastans;
		upd_chain(x,y);
		Mat ans=tree[rot[1]].sum;
		wr(lastans=max(ans.mat[0][0],ans.mat[1][0])),pr;
	}
	return 0;
}

P5024 [NOIP 2018 提高组] 保卫王国

已知最小点覆盖 = 全集 - 最大独立集,转成模版题就做完了,对于必须选和不能选,改一下 \(a_i\) 的取值就好了。

code

posted @ 2025-03-04 22:18  Wei_Han  阅读(10)  评论(0)    收藏  举报