2019.02.16 bzoj5466: [Noip2018]保卫王国(链分治+ddp)

时间:2022-03-02 05:47:19

传送门

题意简述:

mmm次询问,每次规定两个点必须选或者不选,求树上的带权最小覆盖。


思路:

考虑链分治+ddpddpddp

仍然是熟悉的套路,先考虑没有修改的状态和转移:

令fi,0/1f_{i,0/1}fi,0/1​表示强制iii不选/选时iii为根子树的带权最小覆盖。

显然有:

fi,0=∑v∈sonfv,1f_{i,0}=\sum_{v\in son}f_{v,1}fi,0​=∑v∈son​fv,1​

fi,1=valp+∑v∈sonmin{fv,0,fv,1}f_{i,1}=val_p+\sum_{v\in son}min\{f_{v,0},f_{v,1}\}fi,1​=valp​+∑v∈son​min{fv,0​,fv,1​}

这两个转移式。

现在分轻重儿子转移因此再定义两个状态:

令gi,0/1g_{i,0/1}gi,0/1​表示强制iii不选/选时iii为根子树去掉iii重儿子为根子树的带权最小覆盖。

gi,0=∑v∈son,v̸=hsonfv,1g_{i,0}=\sum_{v\in son,v\not=hson}f_{v,1}gi,0​=∑v∈son,v̸​=hson​fv,1​

gi,1=valp+∑v∈son,v̸=hsonmin{fv,0,fv,1}g_{i,1}=val_p+\sum_{v\in son,v\not=hson}min\{f_{v,0},f_{v,1}\}gi,1​=valp​+∑v∈son,v̸​=hson​min{fv,0​,fv,1​}

于是我们将fff的转移式进行变换:

fi,0=gi,0+fhson,1f_{i,0}=g_{i,0}+f_{hson,1}fi,0​=gi,0​+fhson,1​

fi,1=gi,1+vali+min{fhson,0,fhson,1}f_{i,1}=g_{i,1}+val_i+min\{f_{hson,0},f_{hson,1}\}fi,1​=gi,1​+vali​+min{fhson,0​,fhson,1​}

再把这个写成矩阵的形式:

(fi,0fi,1)=(∞gi,0gi,1+valigi,1+vali)(fhson,0fhson,1)
\left(
\begin{matrix}
f_{i,0}\\
f_{i,1}\\
\end{matrix}
\right)=
\left(
\begin{matrix}
\infin&g_{i,0}\\
g_{i,1}+val_i&g_{i,1}+val_i\\
\end{matrix}
\right)
\left(
\begin{matrix}
f_{hson,0}\\
f_{hson,1}\\
\end{matrix}
\right)
(fi,0​fi,1​​)=(∞gi,1​+vali​​gi,0​gi,1​+vali​​)(fhson,0​fhson,1​​)

注意要重定义矩阵运算时的加法和乘法:

乘法⇒\Rightarrow⇒加法

加法⇒\Rightarrow⇒取minminmin

然后对于每个链开一棵线段树维护即可。

常数巨大 没错bzojRank最后一个就是我常数巨大的树剖写法

代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pii;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,fa[N],siz[N],top[N],hson[N],bot[N],num[N],pred[N],tot=0;
ll f[N][2],g[N][2],a[N];
vector<int>e[N];
void dfs1(int p){
	siz[p]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
void dfs2(int p,int tp){
	top[p]=tp,pred[num[p]=++tot]=p,bot[tp]=p;
	f[p][0]=g[p][0]=g[p][1]=0,f[p][1]=a[p];
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p]||v==hson[p])continue;
		dfs2(v,v);
		g[p][0]+=f[v][1];
		g[p][1]+=min(f[v][0],f[v][1]);
	}
	f[p][0]=g[p][0]+f[hson[p]][1];
	f[p][1]=g[p][1]+a[p]+min(f[hson[p]][0],f[hson[p]][1]);
}
const ll inf=1e15,Inf=1e17;
struct Mat{
	ll a[2][2];
	inline ll*operator[](const int&k){return a[k];}
	inline void clear(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=Inf;}
	friend inline Mat operator*(Mat A,Mat B){
		Mat ret;
		ret.clear();
		for(ri i=0;i<2;++i)for(ri k=0;k<2;++k)if(A[i][k]^Inf)
		for(ri j=0;j<2;++j)if(B[k][j]^Inf)ret[i][j]=min(ret[i][j],A[i][k]+B[k][j]);
		return ret;
	}
};
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Node{
		Mat trans;
		int l,r;
		ll g[2],v,f[2];
		inline void init(){
			trans[0][0]=Inf,trans[0][1]=g[0];
			trans[1][0]=g[1]+v,trans[1][1]=g[1]+v;
		}
		inline pii val(){return pii(f[0],f[1]);}
	}T[N<<2];
	inline void Set(int p){
		int k=pred[T[p].l];
		T[p].g[0]=g[k][0],T[p].g[1]=g[k][1],T[p].v=a[k];
		T[p].f[0]=f[k][0],T[p].f[1]=f[k][1];
		T[p].init();
	}
	inline void pushup(int p){T[p].trans=T[lc].trans*T[rc].trans;}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r;
		if(l==r)return Set(p);
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline void update(int p,int k){
		if(T[p].l==T[p].r)return Set(p);
		update(k<=mid?lc:rc,k),pushup(p);
	}
	inline Mat query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].trans;
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return query(lc,ql,qr)*query(rc,ql,qr);
	}
	inline pii query(int p,int k){
		if(T[p].l==T[p].r)return T[p].val();
		return query(k<=mid?lc:rc,k);
	}
}
inline pii ask(int p){
	int bt=bot[top[p]];
	pii ret=SGT::query(1,num[bt]);
	if(bt==p)return ret;
	Mat upd=SGT::query(1,num[p],num[bt]-1);
	return pii(min(ret.fi+upd[0][0],ret.se+upd[0][1]),min(ret.fi+upd[1][0],ret.se+upd[1][1]));
}
inline void update(int p,ll x){
	f[p][1]+=x-a[p],a[p]=x;
	while(p){
		int ft=fa[top[p]],tp=top[p];
		if(ft){
			pii tmp=ask(tp);
			g[ft][0]-=tmp.se;
			g[ft][1]-=min(tmp.fi,tmp.se);
		}
		SGT::update(1,num[p]);
		if(ft){
			pii tmp=ask(tp);
			g[ft][0]+=tmp.se;
			g[ft][1]+=min(tmp.fi,tmp.se);
			f[ft][0]=g[ft][0]+f[hson[ft]][1];
			f[ft][1]=g[ft][1]+a[p]+min(f[hson[ft]][0],f[hson[ft]][1]);
		}
		p=ft;
	}
}
char s[5];
int main(){
	n=read(),m=read();
	scanf("%s",s);
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	dfs1(1),dfs2(1,1),SGT::build(1,1,n);
	for(ri x,y,t1,t2,vx,vy;m;--m){
		x=read(),t1=read(),y=read(),t2=read();
		if(!t1&&!t2&&(fa[x]==y||fa[y]==x)){puts("-1");continue;}
		vx=a[x],vy=a[y];
		update(x,t1?a[x]-inf:a[x]+inf),update(y,t2?a[y]-inf:a[y]+inf);
		pii tmp=ask(1);
		ll ans=min(tmp.fi,tmp.se);
		if(t1)ans+=inf;
		if(t2)ans+=inf;
		cout<<ans<<'\n';
		update(x,vx),update(y,vy);
	}
	return 0;
}

2019.02.16 bzoj5466: [Noip2018]保卫王国(链分治+ddp)的更多相关文章

  1. BZOJ5466 NOIP2018保卫王国(倍增&plus;树形dp)

    暴力dp非常显然,设f[i][0/1]表示i号点不选/选时i子树内的答案,则f[i][0]=Σf[son][1],f[i][1]=a[i]+Σmin(f[son][0],f[son][1]). 注意到 ...

  2. &lbrack;BZOJ5466&rsqb;&lbrack;NOIP2018&rsqb;保卫王国 倍增

    题面 首先可以写一个暴力dp的式子,非常经典的树形dp \(dp[i][0]\)表示\(i\)这个点没有驻军,\(dp[i][1]\)就是有驻军,\(j\)是\(i\)的孩子.那么显然: \[ \be ...

  3. 竞赛题解 - NOIP2018 保卫王国

    \(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...

  4. 2019&period;02&period;16 spoj Query on a tree IV(链分治)

    传送门 题意简述: 捉迷藏强化版(带有边权,可以为负数) 思路:好吧这次我们不用点分树,我们用听起来更屌的链分治. 直接把树剖成若干条重链,这样保证从任意一个点跳到根节点是不会跳超过logloglog ...

  5. 2019&period;02&period;15 bzoj5210&colon; 最大连通子块和(链分治&plus;ddp)

    传送门 题意:支持单点修改,维护子树里的最大连通子块和. 思路: 扯皮: bzojbzojbzoj卡常差评. 网上的题解大多用了跟什么最大子段和一样的转移方法. 但是我们实际上是可以用矩阵转移的传统d ...

  6. 2019&period;01&period;04 洛谷P4719 【模板】动态dp(链分治&plus;ddp)

    传送门 ddpddpddp模板题. 题意简述:给你一棵树,支持修改一个点,维护整棵树的最大带权独立集. 思路: 我们考虑如果没有修改怎么做. 貌似就是一个sbsbsb树形dpdpdp,fi,0f_{i ...

  7. &lbrack;NOIP2018&rsqb;保卫王国 题解

    NOIP2018提高组D2T3 ddp虽然好想,但是码量有点大(其实是我不会),因此本文用倍增优化树形DP来解决本题. 题意分析 给一棵树染色,每个节点染色需要一定的花费,要求相邻两个节点至少要有一个 ...

  8. NOIP2018保卫王国

    题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖. 题解 ZZ码农题. 要用动态dp做,这题就是板子,然鹅并不会,留坑代填. 因为没有修改,所以可以静态倍增. 我们先做一遍 ...

  9. 【比赛】NOIP2018 保卫王国

    DDP模板题 #include<bits/stdc++.h> #define ui unsigned int #define ll long long #define db double ...

随机推荐

  1. ASP&period;NET MVC自定义验证Authorize Attribute

    前几天Insus.NET有在数据库实现过对某一字段进行加密码与解密<使用EncryptByPassPhrase和DecryptByPassPhrase对MS SQLServer某一字段时行加密和 ...

  2. 用nexus搭建maven私服

    首先介绍一下背景,公司访问外网有限制,项目组大部分人员不能访问maven的central repository,因此在局域网里找一台有外网权限的机器,搭建nexus私服,然后开发人员连到这台私服上 环 ...

  3. 【大数据】Summingbird(Storm &plus; Hadoop)的demo运行

    一.前言 为了运行summingbird demo,笔者走了很多的弯路,并且在国内基本上是查阅不到任何的资料,耗时很久才搞定了demo的运行.真的是一把辛酸泪,有兴趣想要研究summingbird的园 ...

  4. 25佳漂亮的结婚邀请 &amp&semi; 婚礼请柬网站设计

    互联网给我们的生活带来了巨大的变化,越来越多的事情可以通过网络完成.下面向大家分享一组结婚邀请网站以及婚礼请柬网站的设计案例,如果你也正想制作这样的网站,相信这些优秀案例能够带给你很大的帮助. 您可能 ...

  5. jetty 最后版本类库树, 基本上大多数应用都够了

    d:\jetty-distribution-8.1.17.v20150415\lib\annotations\javax.annotation-1.1.0.v201108011116.jarjavax ...

  6. redis 一主二从三哨兵

    总体部署 一主二从三哨兵 ip地址分配分别为 主 127.0.0.1:6379 从 127.0.0.1:6389 从 127.0.0.1:6399 哨兵 127.0.0.1:26379 哨兵 127. ...

  7. The Guardian’s Migration from MongoDB to PostgreSQL on Amazon RDS

    转载一片mongodb 迁移pg 数据库的文章 原文:https://www.infoq.com/news/2019/01/guardian-mongodb-postgresql The Guardi ...

  8. python 面向对象编程 之 单例模式

    单例模式三种实现方式: 单例模式:单例模式是解决系统资源浪费的一种方案,是指一个类实例化后可以多次使用此对象. 单例模式应用场景:数据库操作.日志.后台打印 # settings.py# Host=' ...

  9. 使用外部属性文件配置Bean以及Bean的生命周期方法

    1.使用外部属性文件配置Bean 在配置文件里配置 Bean 时, 有时需要在 Bean 的配置里混入系统部署的细节信息(例如: 文件路径, 数据源配置信息等). 而这些部署细节实际上需要和 Bean ...

  10. 与FPGA相关的独热码

    独热码在状态机里面使用比价广泛,这一块有些人爱用,有些人嫌烦,有时候可以用用格雷码跳转,不过格雷码只支持那种一步到底的,中间有分支就不好做了,所以后来还是回到了独热码的正道上. 说白了独热码的使用,在 ...