NOIp 2018 提高组 Day2 T3 保卫王国 题解

题目传送门

题目大意: 我去好长啊不写了qwq

题解

NOIp 太恶心了,又出了一道模板题,这告诉了我们多刷板子的重要性。

没错,这是个动态dp的模板。

这题的转移方程为:
{ f i , 0 = ∑ f s o n , 1 f i , 1 = v a l i + ∑ max ⁡ ( f s o n , 0 , f s o n , 1 ) \begin{cases} f_{i,0}=\sum f_{son,1}\\ f_{i,1}=val_i+\sum \max(f_{son,0},f_{son,1}) \end{cases} {fi,0=fson,1fi,1=vali+max(fson,0,fson,1)

用树链剖分维护一下, g i , 0 g_{i,0} gi,0 g i , 1 g_{i,1} gi,1 分别表示不选 i i i 和选 i i i 的非重子树的最优解,那么有( s o n son son 表示重儿子):
( g i , 1 + v a l i g i , 1 + v a l i g i , 0 i n f ) × ( f s o n , 1 f s o n , 0 ) = ( f i , 1 f i , 0 ) \left( \begin{array}{c} g_{i,1}+val_i & g_{i,1}+val_i\\ g_{i,0} & inf \end{array} \right)\times \left( \begin{array}{c} f_{son,1}\\ f_{son,0}\\ \end{array} \right)= \left( \begin{array}{c} f_{i,1}\\ f_{i,0}\\ \end{array} \right) (gi,1+valigi,0gi,1+valiinf)×(fson,1fson,0)=(fi,1fi,0)

然后维护 g g g 就好了。

(看不懂的话请务必点开上面的连接把板子先学了)

对于每个询问,将这两个点的值改成 i n f inf inf − i n f -inf inf 即可使得强制选或不选。

代码如下(由于懒得卡常于是吸了口氧qwq):

#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
#define maxn 100010
#define ll long long
#define inf 999999999

int n,m;
char type[5];
int val[maxn];
struct edge{int y,next;};
edge e[2*maxn];
int first[maxn];
void buildroad(int x,int y)
{
	static int len=0;
	e[++len]=(edge){y,first[x]};
	first[x]=len;
}
map<int,map<int,bool> >mmp;
int fa[maxn],size[maxn],mson[maxn];
void dfs1(int x)
{
	size[x]=1;
	for(int i=first[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x])continue;
		fa[y]=x;
		dfs1(y);
		size[x]+=size[y];
		if(size[y]>size[mson[x]])mson[x]=y;
	}
}
int now[maxn],old[maxn],id=0,top[maxn];
int be[maxn],bottom[maxn],cnt=0;
void dfs2(int x,int tp,int belong)
{
	now[x]=++id;old[id]=x;top[x]=tp;be[x]=belong;
	if(mson[x]==0){bottom[belong]=x;return;}
	dfs2(mson[x],tp,belong);
	for(int i=first[x];i;i=e[i].next)
	if(e[i].y!=fa[x]&&e[i].y!=mson[x])dfs2(e[i].y,e[i].y,++cnt);
}
ll g[maxn][2][2],f[maxn][2];
/*
g[i][1] , g[i][1]
g[i][0] , inf
*/
void dp(int x)
{
	g[x][0][0]=val[x];g[x][1][1]=inf;
	f[x][1]=val[x];
	for(int i=first[x];i;i=e[i].next)
	{
		int y=e[i].y;
		if(y==fa[x])continue;
		dp(y);
		f[x][0]+=f[y][1];
		f[x][1]+=min(f[y][0],f[y][1]);
		if(y!=mson[x])
		{
			g[x][0][0]+=min(f[y][0],f[y][1]);
			g[x][1][0]+=f[y][1];
		}
	}
	g[x][0][1]=g[x][0][0];
}
struct matrix{
	ll a[2][2];
	matrix(){memset(a,63,sizeof(a));}
	void set(ll s[2][2])
	{
		for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		a[i][j]=s[i][j];
	}
	matrix operator *(const matrix b)
	{
		matrix c;
		for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
		for(int k=0;k<2;k++)
		c.a[i][j]=min(c.a[i][j],a[i][k]+b.a[k][j]);
		return c;
	}
};
struct node{
	int l,r;
	matrix z;
	node *zuo,*you;
	node(int x,int y)
	{
		l=x,r=y;
		if(l<r)
		{
			int mid=l+r>>1;
			zuo=new node(l,mid);
			you=new node(mid+1,r);
			z=zuo->z*you->z;
		}
		else zuo=you=NULL,z.set(g[old[l]]);
	}
	void change(int x)
	{
		if(l==r){z.set(g[old[l]]);return;}
		int mid=l+r>>1;
		if(x<=mid)zuo->change(x);
		else you->change(x);
		z=zuo->z*you->z;
	}
	matrix ask(int x,int y)
	{
		if(l==x&&r==y)return z;
		int mid=l+r>>1;
		if(y<=mid)return zuo->ask(x,y);
		else if(x>=mid+1)return you->ask(x,y);
		else return zuo->ask(x,mid)*you->ask(mid+1,y);
	}
};
node *root=NULL;
/*
g_{i,1} g_{i,1}
g_{i,0} inf
*/
matrix before,after;
void change(int x,int y)
{
	g[x][0][0]+=y;g[x][0][1]=g[x][0][0];
	while(x!=0)
	{
		before=root->ask(now[top[x]],now[bottom[be[x]]]);
		root->change(now[x]);
		after=root->ask(now[top[x]],now[bottom[be[x]]]);
		x=fa[top[x]];
		g[x][0][0]+=min(after.a[0][0],after.a[1][0])-min(before.a[0][0],before.a[1][0]);
		g[x][0][1]=g[x][0][0];
		g[x][1][0]+=after.a[0][0]-before.a[0][0];
	}
}

int main()
{
	scanf("%d %d %s",&n,&m,&type);
	for(int i=1;i<=n;i++)
	scanf("%d",&val[i]);
	for(int i=1,x,y;i<n;i++)
	scanf("%d %d",&x,&y),buildroad(x,y),buildroad(y,x),mmp[x][y]=mmp[y][x]=true;
	dfs1(1);
	dfs2(1,1,++cnt);
	dp(1);
	root=new node(1,n);
	for(int i=1,x,xx,y,yy;i<=m;i++)
	{
		scanf("%d %d %d %d",&x,&xx,&y,&yy);
		if(xx==0&&yy==0&&mmp[x][y])
		{
			printf("-1\n");
			continue;
		}
		ll ans=0;
		if(xx==0)change(x,inf-val[x]);
		else change(x,-inf-val[x]),ans+=inf+val[x];
		if(yy==0)change(y,inf-val[y]);
		else change(y,-inf-val[y]),ans+=inf+val[y];
		
		matrix re=root->ask(now[1],now[bottom[be[1]]]);
		if(x==1)ans+=re.a[(xx^1)][0];
		else if(y==1)ans+=re.a[(yy^1)][0];
		else ans+=min(re.a[0][0],re.a[1][0]);
		printf("%lld\n",ans);
		
		change(x,val[x]-(xx?-inf:inf));change(y,val[y]-(yy?-inf:inf));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值