暴力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)的更多相关文章

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

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

  2. [NOIP2018]保卫王国(树形dp+倍增)

    我的倍增解法吊打动态 \(dp\) 全局平衡二叉树没学过 先讲 \(NOIP\) 范围内的倍增解法. 我们先考虑只有一个点取/不取怎么做. \(f[x][0/1]\) 表示取/不取 \(x\) 后,\ ...

  3. NOIP2018 保卫王国(动态DP)

    题意 求最小权值点覆盖. mmm次询问,每次给出两个点,分别要求每个点必须选或必须不选,输出每次的最小权值覆盖或者无解输出−1-1−1 题解 强制选或者不选可以看做修改权值为±∞\pm\infin±∞ ...

  4. 2019.02.16 bzoj5466: [Noip2018]保卫王国(链分治+ddp)

    传送门 题意简述: mmm次询问,每次规定两个点必须选或者不选,求树上的带权最小覆盖. 思路: 考虑链分治+ddpddpddp 仍然是熟悉的套路,先考虑没有修改的状态和转移: 令fi,0/1f_{i, ...

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

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

  6. 【NOIP 2018】保卫王国(动态dp / 倍增)

    题目链接 这个$dark$题,嗯,不想说了. 法一:动态$dp$ 虽然早有听闻动态$dp$,但到最近才学,如果你了解动态$dp$,那就能很轻松做出这道题了.故利用这题在这里科普一下动态$dp$的具体内 ...

  7. [NOIP2018]保卫王国 题解

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

  8. NOIP2018保卫王国

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

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

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

随机推荐

  1. Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块

    原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...

  2. C#调用c++类的导出函数

    C# 需要调用C++东西,但是有不想做成COM,就只好先导出类中的函数处理. 不能直接调用,需单独导出函数 参考:http://blog.csdn.net/cartzhang/article/deta ...

  3. silverlight 图形报表开发

    前端: <UserControl x:Class="SLThree.CharReport" xmlns="http://schemas.microsoft.com/ ...

  4. 厦门Uber优步司机奖励政策(12月14日到12月20日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  5. jieba结巴分词

    pip install jieba安装jieba模块 如果网速比较慢,可以使用豆瓣的Python源:pip install -i https://pypi.douban.com/simple/ jie ...

  6. 内置方法(item系列)

    class Foo: def __init__(self,name): self.name = name def __getitem__(self, item): # 获取时触发 print('get ...

  7. jmeter基础之录制篇

    一.前言 jmeter如今被越来越多人喜爱的一款测试工具,相比于loadrunner它体积特轻便.jmeter不仅用来做单接口测试,压测还能做性能,主要是一款开源的,可以写一个你需要的插件功能再添加里 ...

  8. Java 输出对象为字符串 工具类

    public static String reflectionToString(Object o){ if(o == null) return StringUtils.EMPTY; StringBui ...

  9. Oracle作业练习题

    第一问 //登陆scott用户 //解锁 alter user scott account unlock; //给用户申请密码 alter user scott identified by tiger ...

  10. JS的六大对象:Global、Math、Number、Date、JSON、console,运行在服务器上方的支持情况分析

    在ASP中使用runat="server"来调用JS的相关函数,代码如下: <script runat="server" language="j ...