[NOIP2018]保卫王国(树形dp+倍增)
我的倍增解法吊打动态 \(dp\) 全局平衡二叉树没学过
先讲 \(NOIP\) 范围内的倍增解法。
我们先考虑只有一个点取/不取怎么做。
\(f[x][0/1]\) 表示取/不取 \(x\) 后,\(x\) 子树内的最小权覆盖集,\(g[x][0/1]\) 表示取/不取 \(x\) 后,除 \(x\) 子树的最小权覆盖集。那么这两个数组可以 \(O(n)\) 预处理出来。
\]
\]
\]
\]
那么我们可以 \(a\) 表示 \(x\) 结点的状态,那么 \(ans=f[x][a]+g[x][a]\)
现在我们考虑两个点取/不取怎么做。
我们发现每次影响的只有两点 \(lca\) 的子树内,所以考虑倍增。
我们用 \(anc\) 表示 \(x\) 结点上跳 \(2^i\) 层的祖先,那么 \(w[x][i][0/1][0/1]\) 表示 \(x\) 取/不取,\(anc\) 取/不取,\(anc\) 子树 \(-\) \(x\) 子树的最小权覆盖集,这个数组我们可以 \(O(n\log n)\) 预处理出来。
我们每次枚举 \(x\) 和 \(anc\) 的四种状态,然后再枚举 \(x\) 结点上跳 \(2^{i-1}\) 层的祖先的状态,然后直接取个 \(min\) 就可以了。
for(int u=0;u<2;u++)
for(int v=0;v<2;v++){
w[i][j][u][v]=inf;
for(int k=0;k<2;k++)
w[i][j][u][v]=min(w[i][j][u][v],w[i][j-1][u][k]+w[tmp][j-1][k][v]);
}
然后再倍增。我们每次想处理 \(w\) 数组一样一直将 \(x\) 结点和 \(y\) 结点向上跳,然后统计答案。
时间复杂度 \(O(n\log n)\)
\(Code\ Below:\)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100000+10;
const ll inf=0x7f7f7f7f7f7f;
int n,m,val[maxn],dep[maxn],fa[maxn][18],head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
ll f[maxn][2],g[maxn][2],w[maxn][18][2][2];char op[10];
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void addedge(int x,int y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int Fa){
dep[x]=dep[Fa]+1;
fa[x][0]=Fa;f[x][1]=val[x];
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==Fa) continue;
dfs1(y,x);
f[x][0]+=f[y][1];
f[x][1]+=min(f[y][0],f[y][1]);
}
}
void dfs2(int x){
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x][0]) continue;
g[y][0]=g[x][1]+f[x][1]-min(f[y][0],f[y][1]);
g[y][1]=min(g[y][0],g[x][0]+f[x][0]-f[y][1]);
dfs2(y);
}
}
ll solve(int a,int x,int b,int y){
if(dep[x]<dep[y]) swap(x,y),swap(a,b);
ll nx[2],ny[2],tx[2]={inf,inf},ty[2]={inf,inf};
tx[a]=f[x][a];ty[b]=f[y][b];
for(int i=17;i>=0;i--)
if(dep[fa[x][i]]>=dep[y]){
nx[0]=nx[1]=inf;
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
nx[j]=min(nx[j],tx[k]+w[x][i][k][j]);
tx[0]=nx[0];tx[1]=nx[1];x=fa[x][i];
}
if(x==y) return tx[b]+g[y][b];
for(int i=17;i>=0;i--)
if(fa[x][i]!=fa[y][i]){
nx[0]=nx[1]=ny[0]=ny[1]=inf;
for(int j=0;j<2;j++)
for(int k=0;k<2;k++){
nx[j]=min(nx[j],tx[k]+w[x][i][k][j]);
ny[j]=min(ny[j],ty[k]+w[y][i][k][j]);
}
tx[0]=nx[0];tx[1]=nx[1];x=fa[x][i];
ty[0]=ny[0];ty[1]=ny[1];y=fa[y][i];
}
int lca=fa[x][0];
ll ans1=f[lca][0]-f[x][1]-f[y][1]+tx[1]+ty[1]+g[lca][0];
ll ans2=f[lca][1]-min(f[x][0],f[x][1])-min(f[y][0],f[y][1])+min(tx[0],tx[1])+min(ty[0],ty[1])+g[lca][1];
return min(ans1,ans2);
}
int main()
{
n=read(),m=read();scanf("%s",op);
int a,x,b,y,tmp;
for(int i=1;i<=n;i++) val[i]=read();
for(int i=1;i<n;i++){
x=read(),y=read();
addedge(x,y);addedge(y,x);
}
dfs1(1,0);dfs2(1);
for(int i=1;i<=n;i++){
tmp=fa[i][0];
w[i][0][0][0]=inf;
w[i][0][0][1]=f[tmp][1]-min(f[i][0],f[i][1]);
w[i][0][1][0]=f[tmp][0]-f[i][1];
w[i][0][1][1]=w[i][0][0][1];
}
for(int j=1;j<=17;j++)
for(int i=1;i<=n;i++){
tmp=fa[i][j-1];
if(fa[tmp][j-1]){
fa[i][j]=fa[tmp][j-1];
for(int u=0;u<2;u++)
for(int v=0;v<2;v++){
w[i][j][u][v]=inf;
for(int k=0;k<2;k++)
w[i][j][u][v]=min(w[i][j][u][v],w[i][j-1][u][k]+w[tmp][j-1][k][v]);
}
}
}
while(m--){
x=read(),a=read(),y=read(),b=read();
if(!a&&!b&&(x==fa[y][0]||y==fa[x][0])){
printf("-1\n");
continue;
}
printf("%lld\n",solve(a,x,b,y));
}
return 0;
}
然后就是 \(O(8n\log^2 n)\) 的树剖+线段树维护矩阵的动态 \(dp\) 了。
发现取/不取我们可以用 \(inf\) 和 \(-inf\) 代替,转化为最大权独立集来做。
\(Code\ Below:\)
#include <bits/stdc++.h>
#define int long long
#define lson (rt<<1)
#define rson (rt<<1|1)
using namespace std;
const int maxn=100000+10;
const int inf=1e10;
int n,m,v[maxn],val[maxn],dp[maxn][2],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,num,ans;
int top[maxn],ed[maxn],siz[maxn],son[maxn],fa[maxn],id[maxn],mp[maxn],tim;
char op[5];
struct Matrix{
int mat[2][2];
Matrix(){
memset(mat,0,sizeof(mat));
}
};
Matrix operator * (const Matrix &a,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.mat[i][j]=max(c.mat[i][j],a.mat[i][k]+b.mat[k][j]);
return c;
}
Matrix a[maxn],sum[maxn<<2];
inline void read(int &x){
x=0;bool f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
if(!f) x=-x;
}
void print(int x){
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
inline void add(int x,int y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f){
siz[x]=1;fa[x]=f;
int maxson=-1;
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==f) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(siz[y]>maxson){
maxson=siz[y];
son[x]=y;
}
}
}
void dfs2(int x,int topf){
id[x]=++tim;
mp[tim]=x;
top[x]=topf;
ed[topf]=x;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
void treedp(int x){
dp[x][0]=0;dp[x][1]=val[x];
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]) continue;
treedp(y);
dp[x][0]+=max(dp[y][0],dp[y][1]);
dp[x][1]+=dp[y][0];
}
}
inline void pushup(int rt){
sum[rt]=sum[lson]*sum[rson];
}
void build(int l,int r,int rt){
if(l == r){
int x=mp[l],b[2]={0,val[x]};
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]||y==son[x]) continue;
b[0]+=max(dp[y][0],dp[y][1]);
b[1]+=dp[y][0];
}
sum[rt].mat[0][0]=sum[rt].mat[0][1]=b[0];
sum[rt].mat[1][0]=b[1];a[x]=sum[rt];
return ;
}
int mid=(l+r)>>1;
build(l,mid,lson);
build(mid+1,r,rson);
pushup(rt);
}
void update(int x,int l,int r,int rt){
if(l == r){
sum[rt]=a[mp[l]];
return ;
}
int mid=(l+r)>>1;
if(x <= mid) update(x,l,mid,lson);
else update(x,mid+1,r,rson);
pushup(rt);
}
Matrix query(int L,int R,int l,int r,int rt){
if(L <= l && r <= R){
return sum[rt];
}
int mid=(l+r)>>1;
if(L > mid) return query(L,R,mid+1,r,rson);
if(R <= mid) return query(L,R,l,mid,lson);
return query(L,R,l,mid,lson)*query(L,R,mid+1,r,rson);
}
void modify(int x,int y){
Matrix u,v;
a[x].mat[1][0]+=y-val[x];val[x]=y;
while(x){
u=query(id[top[x]],id[ed[top[x]]],1,n,1);
update(id[x],1,n,1);
v=query(id[top[x]],id[ed[top[x]]],1,n,1);
x=fa[top[x]];
if(x){
a[x].mat[0][0]+=max(v.mat[0][0],v.mat[1][0])-max(u.mat[0][0],u.mat[1][0]);
a[x].mat[0][1]=a[x].mat[0][0];
a[x].mat[1][0]+=v.mat[0][0]-u.mat[0][0];
}
}
}
signed main()
{
read(n),read(m);
scanf("%s",op+1);
int x,c,d,y;
for(int i=1;i<=n;i++){
read(val[i]);
v[i]=val[i];num+=val[i];
}
for(int i=1;i<n;i++){
read(x),read(y);
add(x,y);add(y,x);
}
dfs1(1,0);dfs2(1,1);
treedp(1);build(1,n,1);
Matrix u;
for(int i=1;i<=m;i++){
read(x),read(c),read(y),read(d);
if(c==0&&d==0&&(x==fa[y]||y==fa[x])){
printf("-1\n");
continue;
}
ans=num;
if(c==0) ans+=inf-val[x];
if(d==0) ans+=inf-val[y];
modify(x,(c==0)?inf:-inf);
modify(y,(d==0)?inf:-inf);
u=query(id[1],id[ed[1]],1,n,1);
ans-=max(u.mat[0][0],u.mat[1][0]);
modify(x,v[x]);modify(y,v[y]);
print(ans);putchar('\n');
}
return 0;
}
[NOIP2018]保卫王国(树形dp+倍增)的更多相关文章
- luogu5024 [NOIp2018]保卫王国 (动态dp)
可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...
- BZOJ 5466: [Noip2018]保卫王国 动态DP
Code: // luogu-judger-enable-o2 #include<bits/stdc++.h> #define ll long long #define lson (now ...
- 竞赛题解 - NOIP2018 保卫王国
\(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...
- 【bzoj2500】幸福的道路 树形dp+倍增RMQ+二分
原文地址:http://www.cnblogs.com/GXZlegend/p/6825389.html 题目描述 小T与小L终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一 ...
- [BZOJ5466][NOIP2018]保卫王国 倍增
题面 首先可以写一个暴力dp的式子,非常经典的树形dp \(dp[i][0]\)表示\(i\)这个点没有驻军,\(dp[i][1]\)就是有驻军,\(j\)是\(i\)的孩子.那么显然: \[ \be ...
- [NOIP2018]保卫王国 题解
NOIP2018提高组D2T3 ddp虽然好想,但是码量有点大(其实是我不会),因此本文用倍增优化树形DP来解决本题. 题意分析 给一棵树染色,每个节点染色需要一定的花费,要求相邻两个节点至少要有一个 ...
- Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]
题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_di ...
- NOIP2018保卫王国
题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖. 题解 ZZ码农题. 要用动态dp做,这题就是板子,然鹅并不会,留坑代填. 因为没有修改,所以可以静态倍增. 我们先做一遍 ...
- BZOJ5466 NOIP2018保卫王国(倍增+树形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]). 注意到 ...
随机推荐
- 【转】你知道C#中的Lambda表达式的演化过程吗?
[转]你知道C#中的Lambda表达式的演化过程吗? 那得从很久很久以前说起了,记得那个时候... 懵懂的记得从前有个叫委托的东西是那么的高深难懂. 委托的使用 例一: 什么是委托? 个人理解:用来传 ...
- mybatis学习 十 动态 SQL
1. 根据方法传入的参数不同执行不同的 SQL 命令.称为动态 SQL, MyBatis 中动态 SQL 就是在 mapper.xml 中添加逻辑判断等. 2. <if>标签 <s ...
- html--笔记day03
html--笔记day03 1.结构标记 1.<header>元素 <header></header> ==> <div id="header ...
- 《从0到1》深度阅读笔记zz
没有人能精准地预测未来,我们只知道两件事:一是世界必然会变得不同:二是现在再好的描述也不能让我们看到清晰的未来. 创业者把成就归功于商业模式和机会窗口,归功于创业者本人拥有的资源和能力,但还有一个最重 ...
- 怎么通过tomcat的catalina.out查看日志
进入tomcat的logs目录下:cd apache-tomcat/logs/ 查看logs下的文件和目录:ll 可以找到catalina.out文件: 实时查看日志: tail -fn 100 ...
- shell 脚本学习之read
Read的一些选项 Read可以带有-a, -d, -e, -n, -p, -r, -t, 和 -s八个选项. -a :将内容读入到数值中 echo -n 'please enter:'read -a ...
- linux 后台执行nohup 命令,终端断开无影响
nohup /root/start.sh & 在shell中回车后提示: [~]$ appending output to nohup.out原程序的的标准输出被自动改向到当前目录下的nohu ...
- 诡异的 ERROR 1045 (28000): Access denied for user 错误
问题描述: 用户已建,权限已赋予.long long ago这个用户是可以正常访问的,但是今天它就不能访问了.报错如下: ERROR 1045 (28000): Access denied for u ...
- 远程算数程序——版本v1.0
很少有需要背诵的程序,但是从这个程序开始,标记的都是必须背诵的. 远程算数程序概述 远程算数程序比较简单,分为服务器端和客户端,客户端发送欲计算的表达式给服务器端,服务端经过计算又返回结果给客户端.如 ...
- 1.3currentThread()方法
该方法可返回代码段正在被哪个线程调用的信息 package com.cky.test; /** * Created by chenkaiyang on 2017/12/2. */ public cla ...