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])。
注意到B的部分分,可以想到每次修改只会对修改点到根的路径上的点的dp值产生影响。
考虑如何优化修改路径这一过程,先看只修改一个点的情况。
由于每次修改并非累积,事实上应该考虑一种预处理来快速得到答案,并且发现其实最终我们只需要f[root][]。容易想到倍增。设f[x][k][0/1][0/1]表示x号点为0/1时其2k级祖先为0/1时这条链上的答案,即其2k级祖先的子树-x的子树的答案。这个东西本身就是可减的,即知道了在x号点子树内的y点选/不选的情况下x子树的答案、y号点选/不选的情况下y子树的答案,将其相减就是x子树去掉y子树的答案。
倍增数组并不难求,显然我们已经有f[x][0][][],在2k-1级祖先那里合并得到2k级的答案,考虑2k-1级祖先选还是不选取个min即可,大约就是floyd/矩乘。回答询问同样也是类似的很正常的倍增。那么只改一个点就能做了。
再考虑改两个点,其实基本类似。两个点倍增求出到他们的lca下方一个点的答案,以此更新lca答案,再从lca倍增跳到根即可。对其中一点是另一点祖先的情况最好特判。听起来不是很复杂但写起来得考虑清楚。
另一种做法是ddp,暂时觉得不太学的动,好像也很久没学新姿势了。感觉倍增做法看上去还是比较noip的,虽然考场上被神仙t2和完全没碰过但知道能做这个题的ddp冲昏头脑肯定想不出来。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define inf 100000000000ll
#define N 100010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,a[N],p[N],fa[N][],deep[N],t;
ll f[N][],g[N][][][];
struct data{int to,nxt,len;
}edge[N<<];
struct data2{ll x,y;int id;};
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k)
{
f[k][]=,f[k][]=a[k];
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
{
deep[edge[i].to]=deep[k]+;
fa[edge[i].to][]=k;
dfs(edge[i].to);
f[k][]+=f[edge[i].to][];
f[k][]+=min(f[edge[i].to][],f[edge[i].to][]);
}
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
{
g[edge[i].to][][][]=inf;
g[edge[i].to][][][]=f[k][]-min(f[edge[i].to][],f[edge[i].to][]);
g[edge[i].to][][][]=f[k][]-f[edge[i].to][];
g[edge[i].to][][][]=f[k][]-min(f[edge[i].to][],f[edge[i].to][]);
}
}
void pre()
{
for (int j=;j<;j++)
{
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
for (int i=;i<=n;i++)
for (int x=;x<;x++)
for (int y=;y<;y++)
g[i][j][x][y]=min(g[i][j-][x][]+g[fa[i][j-]][j-][][y],g[i][j-][x][]+g[fa[i][j-]][j-][][y]);
}
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
for (int j=;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
data2 query(int x,int root,int isup,ll tx,ll ty)
{
data2 u;u.x=tx,u.y=ty;
for (int j=;~j;j--)
if (deep[fa[x][j]]>deep[root])
{
tx=u.x,ty=u.y;
u.x=min(tx+g[x][j][][],ty+g[x][j][][]);
u.y=min(tx+g[x][j][][],ty+g[x][j][][]);
x=fa[x][j];
}
u.id=x;
if (isup)
{
tx=u.x,ty=u.y;
u.x=min(tx+g[x][][][],ty+g[x][][][]);
u.y=min(tx+g[x][][][],ty+g[x][][][]);
}
return u;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5466.in","r",stdin);
freopen("bzoj5466.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();read();
for (int i=;i<=n;i++) a[i]=read();
for (int i=;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
fa[][]=;dfs();
pre();
while (m--)
{
int x=read(),opx=read(),y=read(),opy=read();
if (deep[x]<deep[y]) swap(x,y),swap(opx,opy);
if (opx==&&opy==&&fa[x][]==y) {printf("-1\n");continue;}
int k=lca(x,y);
if (k==y)
{
data2 u=query(x,k,,opx==?f[x][]:inf,opx==?f[x][]:inf);
ll tx=u.x,ty=u.y;
u.x=f[k][]+ty-f[u.id][];
u.y=f[k][]+min(tx,ty)-min(f[u.id][],f[u.id][]);
if (opy==) u.y=inf;else u.x=inf;
if (k!=) u=query(k,,,u.x,u.y);
printf(LL,min(u.x,u.y));
}
else
{
data2 u=query(x,k,,opx==?f[x][]:inf,opx==?f[x][]:inf);
data2 v=query(y,k,,opy==?f[y][]:inf,opy==?f[y][]:inf);
data2 w;
w.x=f[k][]+u.y-f[u.id][]+v.y-f[v.id][];
w.y=f[k][]+min(u.x,u.y)-min(f[u.id][],f[u.id][])+min(v.x,v.y)-min(f[v.id][],f[v.id][]);
w=query(k,,,w.x,w.y);
printf(LL,min(w.x,w.y));
}
}
return ;
}
BZOJ5466 NOIP2018保卫王国(倍增+树形dp)的更多相关文章
- [BZOJ5466][NOIP2018]保卫王国 倍增
题面 首先可以写一个暴力dp的式子,非常经典的树形dp \(dp[i][0]\)表示\(i\)这个点没有驻军,\(dp[i][1]\)就是有驻军,\(j\)是\(i\)的孩子.那么显然: \[ \be ...
- [NOIP2018]保卫王国(树形dp+倍增)
我的倍增解法吊打动态 \(dp\) 全局平衡二叉树没学过 先讲 \(NOIP\) 范围内的倍增解法. 我们先考虑只有一个点取/不取怎么做. \(f[x][0/1]\) 表示取/不取 \(x\) 后,\ ...
- NOIP2018 保卫王国(动态DP)
题意 求最小权值点覆盖. mmm次询问,每次给出两个点,分别要求每个点必须选或必须不选,输出每次的最小权值覆盖或者无解输出−1-1−1 题解 强制选或者不选可以看做修改权值为±∞\pm\infin±∞ ...
- 2019.02.16 bzoj5466: [Noip2018]保卫王国(链分治+ddp)
传送门 题意简述: mmm次询问,每次规定两个点必须选或者不选,求树上的带权最小覆盖. 思路: 考虑链分治+ddpddpddp 仍然是熟悉的套路,先考虑没有修改的状态和转移: 令fi,0/1f_{i, ...
- 竞赛题解 - NOIP2018 保卫王国
\(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...
- 【NOIP 2018】保卫王国(动态dp / 倍增)
题目链接 这个$dark$题,嗯,不想说了. 法一:动态$dp$ 虽然早有听闻动态$dp$,但到最近才学,如果你了解动态$dp$,那就能很轻松做出这道题了.故利用这题在这里科普一下动态$dp$的具体内 ...
- [NOIP2018]保卫王国 题解
NOIP2018提高组D2T3 ddp虽然好想,但是码量有点大(其实是我不会),因此本文用倍增优化树形DP来解决本题. 题意分析 给一棵树染色,每个节点染色需要一定的花费,要求相邻两个节点至少要有一个 ...
- NOIP2018保卫王国
题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖. 题解 ZZ码农题. 要用动态dp做,这题就是板子,然鹅并不会,留坑代填. 因为没有修改,所以可以静态倍增. 我们先做一遍 ...
- luogu5024 [NOIp2018]保卫王国 (动态dp)
可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...
随机推荐
- OracleLinux上安装数据库(DBCA)
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL杂记页 回到顶级页面:PostgreSQL索引页 [作者 高健@博客园 luckyjackgao@gmail. ...
- 成都Uber优步司机奖励政策(2月25日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- 【Hadoop】Seondary NameNode不是备份NameNode!!
昨天和舍友聊天时无意中提起Secondary NameNode,他说这是备用NameNode.我当时就有点疑惑..之后查阅了相关资料和博客,算是基本理解了什么是Secondary NameNode. ...
- Redis系列一 Redis安装
Redis系列一 Redis安装 1.安装所使用的操作系统为Ubuntu16.04 Redis版本为3.2.9 软件一般下载存放目录为/opt,以下命令操作目录均为/opt root@ubunt ...
- 92套AE抖音快闪模板(精品)
包含很多场景和类型,直接用即可,下载地址:百度网盘,https://pan.baidu.com/s/1bRFql1zFWyfpTAwa6MhuPA 内容截图:
- 「日常训练」Woodcutters(Codeforces Round 303 Div.2 C)
这题惨遭被卡..卡了一个小时,太真实了. 题意与分析 (Codeforces 545C) 题意:给定\(n\)棵树,在\(x\)位置,高为\(h\),然后可以左倒右倒,然后倒下去会占据\([x-h,x ...
- TW实习日记:第27天
今天依旧是磨洋工的一天,说真的,被存在各种问题的后端接口把耐心和动力都给磨没了.于是一天就又在沟通接口问题中度过了,完善了一个新功能,将一个新功能开发到了一半.效率可真是够低的,唉.然后不知道为什么突 ...
- (Python爬虫02) 制定爬虫的学习计划了
公司清退是件很让人郁闷的事情,精,气,神 都会受到影响.焦虑的心态,涣散的眼神, 无所适从的若无其事,人周茶凉的快速交接,各种担忧....平静的面孔波涛汹涌的心.... 认识聊天中满满的套路...还有 ...
- lintcode: Check Sum of Square Numbers
Check Sum of Square Numbers Given a integer c, your task is to decide whether there're two integers ...
- Machine Learning笔记整理 ------ (五)决策树、随机森林
1. 决策树 一般的,一棵决策树包含一个根结点.若干内部结点和若干叶子结点,叶子节点对应决策结果,其他每个结点对应一个属性测试,每个结点包含的样本集合根据属性测试结果被划分到子结点中,而根结点包含样本 ...