NOIP2018保卫王国
题目大意:给一颗有点权的树,每次规定两个点选还是不选,求这棵树的最小权点覆盖。
题解
ZZ码农题。
要用动态dp做,这题就是板子,然鹅并不会,留坑代填。
因为没有修改,所以可以静态倍增。
我们先做一遍正常的树形dp,求出g[i][0/1],0/1表示当前节点选或不选。
然后我们再倒腾出一个数组l[i][0/1]表示从当前点作为根,再扣掉当前子树的答案。
然后倍增处理dp[i][j][0/1][0/1]表示从i向上2i长度的链,起点和终点的选择情况,表示以下区域的答案。

比如这条黑色的链,它表示的是黄圈里的所有点的答案。
然后对于一个询问,我们可以跳LCA,边跳变统计答案,这样我们就可以统计出以LCA为根的子树的答案,在加上之前处理过的l数组的答案,就可以吧答案算全了。
NOIP考这种****题有意思吗?
代码
#include<iostream>
#include<cstdio>
#define N 100009
using namespace std;
typedef long long ll;
int tot,head[N],deep[N],p[N][],n,m;
char typef[];
ll dp[N][][][],w[N],f[][],g[][],pr[N][],lian[N][],pr2[N][];
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct node{int n,to;}e[N<<];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
inline void hb(int x,int y,int l){
dp[x][l][][]=min(dp[x][l-][][]+dp[y][l-][][],dp[x][l-][][]+dp[y][l-][][]);
dp[x][l][][]=min(dp[x][l-][][]+dp[y][l-][][],dp[x][l-][][]+dp[y][l-][][]);
dp[x][l][][]=min(dp[x][l-][][]+dp[y][l-][][],dp[x][l-][][]+dp[y][l-][][]);
dp[x][l][][]=min(dp[x][l-][][]+dp[y][l-][][],dp[x][l-][][]+dp[y][l-][][]);
}
void dfs(int u,int fa){
for(int i=;(<<i)<=deep[u];++i){
p[u][i]=p[p[u][i-]][i-];
hb(u,p[u][i-],i);
}
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;deep[v]=deep[u]+;p[v][]=u;
dp[v][][][]=pr[u][]-min(pr[v][],pr[v][]);
dp[v][][][]=pr[u][]-pr[v][];
dp[v][][][]=pr[u][]-min(pr[v][],pr[v][]);
dfs(v,u);
}
}
void predfs(int u,int fa){
pr[u][]=;pr[u][]=w[u];
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;predfs(v,u);
pr[u][]+=pr[v][];pr[u][]+=min(pr[v][],pr[v][]);
}
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
int v=e[i].to;
// pr2[v][0]=pr2[u][1];pr2[v][1]=min(pr2[u][0],pr2[u][1]);
// lian[v][0]=pr2[v][0]-pr[v][0];lian[v][1]=pr2[v][1]-pr[v][1];
lian[v][]=lian[u][]+pr[u][]-min(pr[v][],pr[v][]);
lian[v][]=min(lian[v][],lian[u][]+pr[u][]-pr[v][]);
dfs2(v,u);
}
}
inline ll getlca(int a,int b,int x,int y){
if(deep[a]<deep[b])swap(a,b),swap(x,y);
// cout<<a<<" "<<b<<" "<<x<<" "<<y<<endl;
ll ans=pr[a][x]; //cout<<ans<<endl;
f[][x]=;int now=,pre=;
for(int i=;i>=;--i)if(deep[a]-(<<i)>=deep[b]){
f[now][]=min(f[pre][]+dp[a][i][][],f[pre][]+dp[a][i][][]);
f[now][]=min(f[pre][]+dp[a][i][][],f[pre][]+dp[a][i][][]);
swap(now,pre);a=p[a][i];
}
if(a==b)return ans+f[pre][y]+lian[a][y];
g[pre][y]=;ans+=pr[b][y];//cout<<ans<<endl;
for(int i=;i>=;--i)if(p[a][i]!=p[b][i]){
f[now][]=min(f[pre][]+dp[a][i][][],f[pre][]+dp[a][i][][]);
f[now][]=min(f[pre][]+dp[a][i][][],f[pre][]+dp[a][i][][]);
g[now][]=min(g[pre][]+dp[b][i][][],g[pre][]+dp[b][i][][]);
g[now][]=min(g[pre][]+dp[b][i][][],g[pre][]+dp[b][i][][]);
swap(now,pre);a=p[a][i];b=p[b][i];
}
swap(now,pre);
ll ans1=f[now][]+g[now][]+pr[p[a][]][]-pr[a][]-pr[b][];
ll ans2=min(f[now][],f[now][])+min(g[now][],g[now][])+pr[p[a][]][]-min(pr[a][],pr[a][])-min(pr[b][],pr[b][]);
return ans+min(ans1+lian[p[a][]][],ans2+lian[p[a][]][]);
}
int main(){
n=rd();m=rd();scanf("%s",typef);int u,v;
for(int i=;i<=n;++i)w[i]=rd();
for(int i=;i<=n;++i)
for(int j=;j<=;++j)
for(int k=;k<=;++k)for(int l=;l<=;++l)dp[i][j][k][l]=1e12;
for(int i=;i<n;++i){u=rd();v=rd();add(u,v);add(v,u);}
predfs(,);
pr2[][]=pr[][];pr2[][]=pr[][];dfs2(,);
dfs(,);int a,x,b,y;
/* for(int i=1;i<=n;++i){
cout<<i<<" ";
for(int j=0;j<=1;++j)cout<<lian[i][j]<<" ";cout<<endl;
}*/
/* for(int i=1;i<=n;++i){
cout<<i<<endl;
for(int j=0;(1<<j)<=deep[i];++j)cout<<dp[i][j][0][0]<<" "<<dp[i][j][0][1]<<" "<<dp[i][j][1][0]<<" "<<dp[i][j][1][1]<<endl;
}*/
while(m--){
a=rd();x=rd();b=rd();y=rd();
for(int i=;i<=;++i)for(int j=;j<=;++j)f[i][j]=g[i][j]=1e12;
ll ans=getlca(a,b,x,y);
if(ans>1e10)puts("-1");else printf("%lld\n",ans);
}
return ;
}
NOIP2018保卫王国的更多相关文章
- 竞赛题解 - NOIP2018 保卫王国
\(\mathcal{NOIP2018}\) 保卫王国 - 竞赛题解 按某一个炒鸡dalao名曰 taotao 的话说: \(\ \ \ \ \ \ \ \ \ "一道sb倍增题" ...
- [NOIP2018]保卫王国 题解
NOIP2018提高组D2T3 ddp虽然好想,但是码量有点大(其实是我不会),因此本文用倍增优化树形DP来解决本题. 题意分析 给一棵树染色,每个节点染色需要一定的花费,要求相邻两个节点至少要有一个 ...
- 【比赛】NOIP2018 保卫王国
DDP模板题 #include<bits/stdc++.h> #define ui unsigned int #define ll long long #define db double ...
- luogu5024 [NOIp2018]保卫王国 (动态dp)
可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...
- 2019.02.16 bzoj5466: [Noip2018]保卫王国(链分治+ddp)
传送门 题意简述: mmm次询问,每次规定两个点必须选或者不选,求树上的带权最小覆盖. 思路: 考虑链分治+ddpddpddp 仍然是熟悉的套路,先考虑没有修改的状态和转移: 令fi,0/1f_{i, ...
- [NOIP2018]保卫王国
嘟嘟嘟 由于一些知道的人所知道的,不知道的人所不知道的原因,我来发NOIP2018day2T3的题解了. (好像我只是个搬运工--) 这题真可以叫做NOIplus了,跟其他几道比较水的题果然不一样,无 ...
- [NOIP2018]保卫王国(树形dp+倍增)
我的倍增解法吊打动态 \(dp\) 全局平衡二叉树没学过 先讲 \(NOIP\) 范围内的倍增解法. 我们先考虑只有一个点取/不取怎么做. \(f[x][0/1]\) 表示取/不取 \(x\) 后,\ ...
- 「NOIP2018 保卫王国」
题目 强制选点我们可以把那个点权搞成\(-inf\),强制不选我们搞成\(inf\),之后就真的成为动态\(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]). 注意到 ...
随机推荐
- Jenkins Installing and migration
JAVA_Zookeeper_hadoop - CSDN博客https://blog.csdn.net/wangmuming Installing Jenkins on Red Hat distrib ...
- 剑指offer(4)
题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2 ...
- python之路--线程的其他方法
一 . current_thread的用法 import threading import time from threading import Thread, current_thread def ...
- com.alibaba的fastjson简介
fastjson简介 Fastjson是一个Java语言编写的高性能功能完善的JSON库.它采用一种“假定有序快速匹配”的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSO ...
- DAY08、文件操作
一.文件操作模式汇总: 主模式: r:读模式 w:写模式(无创建,有清空) a:追加(有创建的功能) x:写,必须自己创建文件,否则报错 从模式: t:文本操作(默认模式)r >rt,w> ...
- css 優先級
!impoetant:1000 行間樣式 id:100 類選擇器.屬性選擇器和偽類:10 元素及偽元素:1 通配選擇器:0 相同優先級的樣式,後來居上. 當超過256種的時候,瀏覽器會不遵守以上優先級 ...
- LODOP直接用base64码输出图片
Lodop中的ADD_PRINT_IMAGE,也可以直接输出base64码图片,不用加img标签,如果加了img标签,会被当做超文本对待,受浏览器引擎解析的影响. 什么时候使用base64码直接输出比 ...
- vue 使用技巧总结 18.11
前言: 在大概学完 vue 整体框架后,有幸接触到花裤衩大神写的 vue-elementUI-admin 模板框架,把这个模板框架当作 demo,跟着 code 一遍,最大的收获是在以逻辑简单的模板熟 ...
- 51-nod(1443)(最短路)
解题思路:最短路+记录前驱和,刚开始一直以为是最短路+MST,结果发现,因为无向图的原因,有些边权很小的边再最短路处理后可能这条边也符合某两个点的最短路径,所以我们觉得这条边也是可以在MST处理中使用 ...
- Nginx stream如何获取ssl信息并反向代理至上游服务器
L:116