我的倍增解法吊打动态 \(dp\) 全局平衡二叉树没学过

先讲 \(NOIP\) 范围内的倍增解法。

我们先考虑只有一个点取/不取怎么做。

\(f[x][0/1]\) 表示取/不取 \(x\) 后,\(x\) 子树内的最小权覆盖集,\(g[x][0/1]\) 表示取/不取 \(x\) 后,除 \(x\) 子树的最小权覆盖集。那么这两个数组可以 \(O(n)\) 预处理出来。

\[f[x][0]+=f[y][1]
\]

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

\[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])
\]

那么我们可以 \(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+倍增)的更多相关文章

  1. luogu5024 [NOIp2018]保卫王国 (动态dp)

    可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...

  2. BZOJ 5466: [Noip2018]保卫王国 动态DP

    Code: // luogu-judger-enable-o2 #include<bits/stdc++.h> #define ll long long #define lson (now ...

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

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

  4. 【bzoj2500】幸福的道路 树形dp+倍增RMQ+二分

    原文地址:http://www.cnblogs.com/GXZlegend/p/6825389.html 题目描述 小T与小L终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一 ...

  5. [BZOJ5466][NOIP2018]保卫王国 倍增

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

  6. [NOIP2018]保卫王国 题解

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

  7. Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]

    题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_di ...

  8. NOIP2018保卫王国

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

  9. 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]). 注意到 ...

随机推荐

  1. samtools flagstat

    samtools flagstat命令简介: 统计输入文件的相关数据并将这些数据输出至屏幕显示.每一项统计数据都由两部分组成,分别是QC pass和QC failed,表示通过QC的reads数据量和 ...

  2. 【算法】Escape

    The students of the HEU are maneuvering for their military training. The red army and the blue army ...

  3. Linux mount命令

    mount 1.作用 mount命令的作用是加载文件系统,它的用权限是超级用户或/etc/fstab中允许的使用者. 2.格式 mount -a [-fv] [-t vfstype] [-n] [-r ...

  4. 别人的Linux私房菜(1)计算机概论

    计算机主板 早期两个网桥控制通信,北桥连接速度比较快的CPU.内存.显卡.南桥连接较慢的接口,如硬盘,USB,网卡等.北桥的控制器集成到了CPU中. CPU工作频率 外频:CPU与外部组件进行数据传输 ...

  5. 【转】python 修改os环境变量

    举一个很简单的例子,如果你发现一个包或者模块,明明是有的,但是会发生这样的错误: >>> from algorithm import *Traceback (most recent ...

  6. python 中 __name__ 的使用

    1. 如果模块是被导入,__name__的值为模块名字2. 如果模块是被直接执行,__name__的值为’__main__’   Py1.py #!/usr/bin/env python def te ...

  7. Python10/22--面向对象编程/类与对象/init函数

    类: 语法: class关键字 类名# 类名规范 大写开头 驼峰命名法class SHOldboyStudent: # 描述该类对象的特征 school = "上海Oldboy" ...

  8. php->是什么意思

    在php中经常会看到这种符号,但是不明白是什么意思,有没有细心点的朋友帮讲讲,请不要说看手册之类的话.如果真给我讲明白了,我还会有加分的哦!比如这个中的符号是什么意思:$sql=$mydami-> ...

  9. The XOR Largest Pair(Tire字典树应用)

    题目链接:传送门 思路:建立一个32位的字典树,对每一个要插入的数字查找它异或的最大值(就是尽量全部二进制的值都相反), 然后获得两个数异或的最大值. #include<iostream> ...

  10. hdu-4300(字符串hash)

    题目链接:传送门 参考文章:传送门 思路: (1)对字符串进行翻译,求出s1(未翻译),s2(已翻译)字符串,并分别对s1,s2进行hash操作. (2)比较s1的后x位和s2的前x位的hash值,求 ...