题目

题目链接:https://www.luogu.com.cn/problem/P5024
Z 国有 \(n\) 座城市,\((n - 1)\) 条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。

Z 国的国防部长小 Z 要在城市中驻扎军队。驻扎军队需要满足如下几个条件:

  • 一座城市可以驻扎一支军队,也可以不驻扎军队。
  • 由道路直接连接的两座城市中至少要有一座城市驻扎军队。
  • 在城市里驻扎军队会产生花费,在编号为 \(i\) 的城市中驻扎军队的花费是 \(p_i\)

小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小 Z 提出了 \(m\) 个要求,每个要求规定了其中两座城市是否驻扎军队。小 Z 需要针对每个要求逐一给出回答。具体而言,如果国王提出的第 \(j\) 个要求能够满足上述驻扎条件(不需要考虑第 \(j\) 个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第 \(j\) 个要求无法满足,则需要输出 \(-1\)。现在请你来帮助小 Z。

思路

如果没有修改操作,用树形 dp 容易求答案。具体的,设 \(f[x][1/0]\) 表示点 \(x\) 选或不选,\(x\) 子树内每一条边都合法的最小值。显然有

\[f[x][0]=\sum_{y\in \operatorname{sub}(x)} f[y][1] \]

\[f[x][1]=p[x]+\sum_{y\in \operatorname{sub}(x)} \min(f[y][0],f[y][1]) \]

那么现在每次询问前要强制两个点选或不选,考虑用动态 dp 解决。
果断树剖,那么令 \(g[x][1/0]\) 表示 \(x\) 的轻儿子全选 / 选或不选均可的最小值。则有

\[f[x][1]=\min(g[x][0]+f[y][1],g[x][0]+f[y][0]) \]

\[f[x][0]=\min(g[x][1]+f[y][1],g[x][0]+\infty) \]

由于树剖剖出的每一条链中,会导致深度小的编号在深度大的前面,所以我们构造出如下矩阵

\[\begin{bmatrix} g[x][0]&g[x][0] \\ g[x][1]&+\infty \end{bmatrix} \times \begin{bmatrix} f[y][1] \\ f[y][0] \end{bmatrix}= \begin{bmatrix} f[x][1] \\ f[x][0] \end{bmatrix} \]

当强制选点的时候,我们就把这个点所对应的矩阵第二行全部加上 \(\infty\),这样就使得 \(f[x][0]\geq\infty\)。强制不取点同理。
时间复杂度 \(O(Qm^3\log n)\)

代码

就这代码我调了两天服了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100010;
const ll Inf=100000000000000LL;
int Q,n,tot,p[N],head[N],id[N],rk[N],top[N],son[N],fa[N],size[N],end[N];
ll f[N][2],g[N][2];
char WYCTQL[5];

struct edge
{
	int next,to;
}e[N*2];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void dfs1(int x,int ff)
{
	f[x][0]=0; f[x][1]=p[x];
	fa[x]=ff; size[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=ff)
		{
			dfs1(v,x);
			f[x][0]+=f[v][1];
			f[x][1]+=min(f[v][1],f[v][0]);
			size[x]+=size[v];
			if (size[v]>size[son[x]]) son[x]=v;
		}
	}
}

void dfs2(int x,int tp)
{
	g[x][0]=p[x]; g[x][1]=0;
	top[x]=tp; id[x]=++tot; rk[tot]=x; end[tp]=x;
	if (son[x]) dfs2(son[x],tp);
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa[x] && v!=son[x])
		{
			dfs2(v,v);
			g[x][0]+=min(f[v][1],f[v][0]);
			g[x][1]+=f[v][1];
		}
	}
}

struct Matrix
{
	ll a[3][3];
	
	Matrix() { a[1][1]=a[1][2]=a[2][1]=a[2][2]=Inf; }
	
	friend Matrix operator *(Matrix a,Matrix b)
	{
		Matrix c;
		for (int i=1;i<=2;i++)
			for (int j=1;j<=2;j++)
				for (int k=1;k<=2;k++)
					c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
		return c;
	}
}mat[N];

struct SegTree
{
	int l[N*4],r[N*4];
	Matrix f[N*4];
	
	void pushup(int x)
	{
		f[x]=f[x*2]*f[x*2+1];
	}
	
	void build(int x,int ql,int qr)
	{
		l[x]=ql; r[x]=qr;
		if (ql==qr)
		{
			f[x]=mat[rk[ql]];
			return;
		}
		int mid=(ql+qr)>>1;
		build(x*2,ql,mid); build(x*2+1,mid+1,qr);
		pushup(x);
	}
	
	void update(int x,int k)
	{
		if (l[x]==k && r[x]==k)
		{
			f[x]=mat[rk[k]];
			return;
		} 
		int mid=(l[x]+r[x])>>1;
		if (k<=mid) update(x*2,k);
			else update(x*2+1,k);
		pushup(x);
	}
	
	Matrix query(int x,int ql,int qr)
	{
		if (l[x]==ql && r[x]==qr)
			return f[x];
		int mid=(l[x]+r[x])>>1;
		if (qr<=mid) return query(x*2,ql,qr);
		if (ql>mid) return query(x*2+1,ql,qr);
		return query(x*2,ql,mid)*query(x*2+1,mid+1,qr);
	}
}seg;

void update(int x)
{
	while (x)
	{
		Matrix last=seg.query(1,id[top[x]],id[end[top[x]]]);
		seg.update(1,id[x]);
		Matrix now=seg.query(1,id[top[x]],id[end[top[x]]]);
		x=fa[top[x]];
		mat[x].a[1][1]+=min(now.a[1][1],now.a[2][1])-min(last.a[1][1],last.a[2][1]);
		mat[x].a[1][2]+=min(now.a[1][1],now.a[2][1])-min(last.a[1][1],last.a[2][1]);
		mat[x].a[2][1]+=now.a[1][1]-last.a[1][1];
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&Q);
	scanf("%s",WYCTQL);
	for (int i=1;i<=n;i++)
		scanf("%d",&p[i]);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	tot=0;
	dfs1(1,0); dfs2(1,1);
	for (int i=1;i<=n;i++)
	{
		mat[i].a[1][1]=mat[i].a[1][2]=g[i][0];
		mat[i].a[2][1]=g[i][1];
	}
	seg.build(1,1,n);	
	while (Q--)
	{
		int a,b,x,y;
		scanf("%d%d%d%d",&a,&x,&b,&y);
		if (x) mat[a].a[2][1]+=Inf,mat[a].a[2][2]+=Inf;
			else mat[a].a[1][1]+=Inf,mat[a].a[1][2]+=Inf;
		if (y) mat[b].a[2][1]+=Inf,mat[b].a[2][2]+=Inf;
			else mat[b].a[1][1]+=Inf,mat[b].a[1][2]+=Inf;
		update(a); update(b);
		
		Matrix matans=seg.query(1,1,id[end[1]]);
		ll ans=min(matans.a[1][1],matans.a[2][1]);
		if (ans<Inf) printf("%lld\n",(ans<0)?(ans+Inf):ans);
			else printf("-1\n");
			
		if (x) mat[a].a[2][1]-=Inf,mat[a].a[2][2]-=Inf;
			else mat[a].a[1][1]-=Inf,mat[a].a[1][2]-=Inf;
		if (y) mat[b].a[2][1]-=Inf,mat[b].a[2][2]-=Inf;
			else mat[b].a[1][1]-=Inf,mat[b].a[1][2]-=Inf;
		update(a); update(b);
	}
	return 0;
}