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 ...
随机推荐
- COGS:1822. [AHOI2013]作业
1822. [AHOI 2013] 作业 ★★★ 输入文件:ahoi2013_homework.in 输出文件:ahoi2013_homework.out 简单对比时间限制:20 s ...
- SIFT 特征点提取算法
SIFT特征点相对于ORB计算速度较慢,在没有GPU加速情况下,无法满足视觉里程计的实时性要求,或者无法运行在手机平台上,但是效果更好,精度更高.在应用时可以择优选取,了解其本质原理的动机是为了自己使 ...
- springboot jpa操作redis
SpringBoot使用Redis缓存 (1)pom.xml引入jar包,如下: <dependency> <groupId>org.springframework.boo ...
- 翻译:利用GDAL生成cogeoff文件
翻译自: Introducing the AWS Lambda Tiler https://hi.stamen.com/stamen-aws-lambda-tiler-blog-post-76fc11 ...
- android学习五 Intent
1.Intent是组件间调用的桥梁. 2.Android系统定义了很多Intent http://developer.android.com/guide/components/intents-c ...
- centos7下安装mysql8.0.12及设置权限
一.mysql版本介绍 mysql的官网为:https://www.mysql.com/ 在官网上可以看到多个版本,主要版本如下, 1.MySQL Community Server 社区版本,开源免费 ...
- yield学习
如果要控制内存占用,最好不要用list来保存中间结果,而是通过iterable对象(range, xrange, generator等)来迭代. yield 使函数变为generator,返回对象 ...
- http tcp udp
HTTP连接 1.HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用 ...
- python3读取csv文件
代码如下 import csv with open('D:\\abc\\userinfo.csv',newline='') as f: reader = csv.reader(f) for row i ...
- 227. Mock Hanoi Tower by Stacks【LintCode java】
Description In the classic problem of Towers of Hanoi, you have 3 towers and N disks of different si ...