[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]). 注意到 ...
随机推荐
- PHP连接SQLServer2012两例
首先放上 PHP连接SQLServer的驱动下载地址 http://php.net/manual/zh/ref.pdo-sqlsrv.php 另外PHP for IIS管理工具 大家可以自己搜索一下 ...
- UGUI控制UI的显示层级
1.调用transform.SetAsLastSibling();将该UI的显示层级调到最上面. 调用transform.SetAsFirstSibling();将该UI的显示层级调到最下面. 2. ...
- 41.App 框架的搭建思路以及代码的规范
本链接 引用别人文章https://www.jianshu.com/p/d553096914ff
- ORA-38301:can not perform DDL/DML over objects in Recycle Bin
一个智障操作,drop一个用户,下面的东西比较多,删得比较慢,然后shell突然关了. 就导致了,删不掉,又不能创建新的用户.出版本要得比较急,就先创建新的用户测试去了. 今天要弄个东西,又想起这个事 ...
- Codeforces 1093 简要题解
文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 GGG题手速慢了没有在比赛的时候码出来233,FFF题居然没想出来? 五道题滚粗. 先谈谈其他几道题. A题 传送门 不小心看错题 直接看奇 ...
- IE 8 浏览器 F12 调试功能无法使用
“按下F12之后,开发人员工具在桌面上看不到,但是任务栏里有显示.将鼠标放在任务栏的开发人员工具上,出现一片透明的区域,选中之后却出不来.将鼠标移动到开发人员工具的缩略图上,右键-最大化,工具就全 ...
- 点击文字弹出一个DIV层窗口代码
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <hea ...
- 成功解决在Python文件上右键菜单无“Edit with IDLE”选项
我电脑是Win7旗舰版,之前电脑上安装的是Python2.6版本的,前两天为了体验一下Microsoft Excel与Python之间互操作, 下载并安装了DataNitro,在安装的时候脑残的安装了 ...
- linux下运算的几种方法
1.expr 1.1 最简单的用法 yan@yan:~$ \* yan@yan:~$ 1.2 bash脚本对于expr yan@yan:~$ cat expr1.sh #!/bin/bash PATH ...
- 2017-2018-1 20155326 《信息安全系统设计基础》第四周学习总结及myod改进版的补交
2017-2018-1 20155326 <信息安全系统设计基础>第四周学习总结及myod改进版的补交 学习内容 补充完成课上没有完成的内容 学习教材附录A,第十章内容 参考别出心裁的Li ...