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]). 注意到 ...
随机推荐
- MYSQL业务数据简单脱敏方案
removesensitiveinformationplan.sh #!/bin/sh #!在模拟库上运行如下脚本 #!生产库crm-db #!模拟库crm-mock #!.备份生产库 mysqldu ...
- PHP常见错误汇总
日常开发和调试的时候,经常会遇到一些错误,光怪陆离的不知所以,所以,特此将错误汇总一下,借鉴!!! 1. 原因分析: 一般可能是该文件出现了问题,检查一下代码和格式,是否出现开始的地方出现了空格,或 ...
- 二、npm scripts
一.执行原理 安装npm 包,会将其package.json bin 字段添加到node_modules bin 里面,创建对应的.cmd文件,因此: 例如: "scripts": ...
- jQuery 事件 - triggerHandler() 方法
定义和用法 triggerHandler() 方法触发被选元素的指定事件类型.但不会执行浏览器默认动作,也不会产生事件冒泡. triggerHandler() 方法与 trigger() 方法类似.不 ...
- Vue 刷新当前页面,并重新加载页面数据
业务场景:在管理后台,在执行完,增,删,改,操作的时候.我们需要刷新一下页面,重载数据.在JQ中我们会用到location.reload()方法,刷新页面:在vue中,这里需要用到一个 provide ...
- 爬虫 之Requests库的详细使用
1.什么是Requests? Requests是用Python语言编写的,基于urllib3来改写的,采用Apache2 Licensed 来源协议的HTTP库. 它比urllib更加方便,可以节约我 ...
- Unable to handle kernel paging request at virtual address
1.Unable to handle kernel paging request at virtual address 00000000 =====>越出内核地址空间范围,原因是由于使用空NUL ...
- myisam和innodb的区别对比
1.MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法.不 ...
- Yii2常用ActiveRecord用法
1.多表连表查询与对象关联查询 public function getWmsCheck(){ return $this->hasOne(\core\models\WmsCheck::classN ...
- 华硕X99-A II 安装使用 志强 XEON E5-1603 v4
刚开始无法启动,Debug灯的数字不停的轮回变换,后来把XMP开关关闭后,就能正常启动了.如果不行,就多关机几次,一般3次以上应该就可以启动开了.之后就能正常使用了.