P5024 保卫王国(动态dp/整体dp/倍增dp)
做法(倍增)
最好写的一种
以下0为不选,1为选
\(f_{i,0/1}\)为\(i\)子树的最小值,\(g_{i,0/1}\)为除i子树外的最小值
\(fh_{i,j,0/1,0/1}\)为确定\(i\)与\(i\)的\(2^j\)级祖先的状态,\(i\)的\(2^j\)祖先不包括i子树的最小值
这个转移挺好想的,故不赘述
- 查询
考虑边倍增爬上去,边处理即可
#include<bits/stdc++.h>
typedef long long LL;
const LL maxn=1e6+9,inf=0x3f3f3f3f3f3f3f3f;
inline LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3)+(x<<1)+c-'0'; c=getchar();
}return x*f;
}
struct node{
LL to,nxt;
}dis[maxn<<1];
LL n,q,num;
LL head[maxn],f[maxn][2],inc[maxn][21],g[maxn][2],fh[maxn][21][2][2],dep[maxn],p[maxn];
char s[3];
inline void Add(LL u,LL v){
dis[++num]=(node){v,head[u]}; head[u]=num;
}
void Dfs1(LL u,LL fa){
inc[u][0]=fa; dep[u]=dep[fa]+1; f[u][1]=p[u];
for(LL i=1;i<=20;++i) inc[u][i]=inc[inc[u][i-1]][i-1];
for(LL i=head[u];i;i=dis[i].nxt){
LL v(dis[i].to); if(v==fa) continue;
Dfs1(v,u);
f[u][0]+=f[v][1]; f[u][1]+=std::min(f[v][0],f[v][1]);
}
}
void Dfs2(LL u,LL fa){
fh[u][0][0][1]=fh[u][0][1][1]=f[fa][1]-std::min(f[u][0],f[u][1]);
fh[u][0][0][0]=inf; fh[u][0][1][0]=f[fa][0]-f[u][1];
for(LL i=1;i<=20;++i)
for(LL x=0;x<=1;++x)
for(LL y=0;y<=1;++y){
fh[u][i][x][y]=inf;
for(LL xx=0;xx<=1;++xx)
fh[u][i][x][y]=std::min(fh[u][i][x][y],fh[inc[u][i-1]][i-1][xx][y]+fh[u][i-1][x][xx]);
}
for(LL i=head[u];i;i=dis[i].nxt){
LL v(dis[i].to); if(v==fa) continue;
g[v][0]=g[u][1]+f[u][1]-std::min(f[v][0],f[v][1]);
g[v][1]=std::min(g[u][0]+f[u][0]-f[v][1],g[v][0]);
Dfs2(v,u);
}
}
inline LL Solve(LL a,LL x,LL b,LL y){
if(dep[a]<dep[b]) std::swap(a,b),std::swap(x,y);
LL aa[2]={inf,inf},ab[2]={inf,inf};
LL ta[2]={inf,inf},tb[2]={inf,inf};
aa[x]=f[a][x]; ab[y]=f[b][y];
for(LL i=20;i>=0;--i){
if(dep[inc[a][i]]>=dep[b]){
for(LL ii=0;ii<=1;++ii){
ta[ii]=inf;
for(LL jj=0;jj<=1;++jj)
ta[ii]=std::min(ta[ii],aa[jj]+fh[a][i][jj][ii]);
}
a=inc[a][i]; aa[1]=ta[1],aa[0]=ta[0];
}
}
if(a==b) return aa[y]+g[a][y];
for(LL i=20;i>=0;--i){
if(inc[a][i]!=inc[b][i]){
for(LL ii=0;ii<=1;++ii){
ta[ii]=tb[ii]=inf;
for(LL jj=0;jj<=1;++jj){
ta[ii]=std::min(ta[ii],aa[jj]+fh[a][i][jj][ii]);
tb[ii]=std::min(tb[ii],ab[jj]+fh[b][i][jj][ii]);
}
}
a=inc[a][i], b=inc[b][i]; aa[1]=ta[1],aa[0]=ta[0]; ab[1]=tb[1],ab[0]=tb[0];
}
}
LL fa=inc[a][0];
LL ans0(aa[1]+ab[1]+(g[fa][0]+f[fa][0]-f[a][1]-f[b][1]));
LL ans1(g[fa][1]+f[fa][1]-std::min(f[a][1],f[a][0])-std::min(f[b][1],f[b][0])+std::min(aa[1],aa[0])+std::min(ab[1],ab[0]));
return std::min(ans0,ans1);
}
int main(){
// freopen("testdata.in","r",stdin);
n=Read(); q=Read(); scanf(" %s",s+1);
for(LL i=1;i<=n;++i) p[i]=Read();
for(LL i=1;i<n;++i){
LL u(Read()),v(Read()); Add(u,v); Add(v,u);
}
Dfs1(1,0); Dfs2(1,0);
while(q--){
LL a(Read()),x(Read()),b(Read()),y(Read());
LL ret(Solve(a,x,b,y));
if(ret>=inf) puts("-1");
else printf("%lld\n",ret);
}
return 0;
}
整体
思维较大,代码细节有点多
f与g与方法一同理得出
考虑类似tarjan离线的处理方式,用并查集,并查集按秩压缩
\(fa_x\)为x暂时在并查集里的父亲,\(h_{x,0/1,0/1}\)表示确定x父亲与x的状态,x父亲这棵子树的最小值,状态转移压缩时处理
当我们得出(x,y)的祖先时,把查询再丢到祖先u去
- 查询
对于u存在于(x,y)的情况单独讨论,因为这样u的状态唯一
否则枚举u状态,\(min(g[u][0]+h[a][0][x]+h[b][0][y]-f[u][0],g[u][1]+h[a][1][x]+h[b][1][y]-f[u][1])\)
#include<bits/stdc++.h>
typedef long long LL;
const LL maxn=1e6+9,inf=0x3f3f3f3f3f3f3f3f;
inline LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3)+(x<<1)+c-'0'; c=getchar();
}return x*f;
}
struct node{
LL to,nxt;
}dis[maxn<<1];
struct qy{
LL a,x,b,y,id;
};
LL n,q,num;
LL head[maxn],f[maxn][2],g[maxn][2],h[maxn][2][2],dep[maxn],p[maxn],fa[maxn],nx[2][2],ans[maxn];
std::vector<qy> Qy[maxn],Ans[maxn];
char s[3];
inline void Add(LL u,LL v){
dis[++num]=(node){v,head[u]}; head[u]=num;
}
void Dfs1(LL u,LL ff){
f[u][1]=p[u];
for(LL i=head[u];i;i=dis[i].nxt){
LL v(dis[i].to); if(v==ff) continue;
Dfs1(v,u);
f[u][0]+=f[v][1]; f[u][1]+=std::min(f[v][0],f[v][1]);
}
}
void Dfs2(LL u,LL ff){
for(LL i=head[u];i;i=dis[i].nxt){
LL v(dis[i].to); if(v==ff) continue;
g[v][0]=g[u][1]+f[u][1]-std::min(f[v][0],f[v][1]);
g[v][1]=std::min(g[u][0]+f[u][0]-f[v][1],g[v][0]);
Dfs2(v,u);
}
}
inline void Union(LL x,LL y){
fa[x]=y; LL tmp(f[y][1]-std::min(f[x][0],f[x][1]));
h[x][0][0]=inf; h[x][0][1]=f[y][0]; h[x][1][1]=tmp+f[x][1]; h[x][1][0]=tmp+f[x][0];
}
void Find(LL x){
if(fa[x]==fa[fa[x]]) return;
Find(fa[x]);
LL up(fa[x]);
for(LL i=0;i<=1;++i)
for(LL j=0;j<=1;++j){
nx[i][j]=h[x][i][j]; h[x][i][j]=inf;
}
for(LL i=0;i<=1;++i)
for(LL j=0;j<=1;++j)
for(LL k=0;k<=1;++k)
h[x][i][j]=std::min(h[x][i][j],nx[k][j]+h[up][i][k]-f[up][k]);
fa[x]=fa[fa[x]];
}
void Dfs3(LL u,LL ff){
for(LL i=head[u];i;i=dis[i].nxt){
LL v(dis[i].to); if(v==ff) continue;
Dfs3(v,u);
Union(v,u);
}
for(LL i=0;i<Qy[u].size();++i){
LL a(Qy[u][i].a),b(Qy[u][i].b);
if(a==u) a=b;
Find(a);
if(a!=fa[a]){
Ans[fa[a]].push_back(Qy[u][i]);
}
}
for(LL i=0;i<Ans[u].size();++i){
LL a(Ans[u][i].a),b(Ans[u][i].b),x(Ans[u][i].x),y(Ans[u][i].y),id(Ans[u][i].id);
Find(a); Find(b);
if(u!=a && u!=b) ans[id]=std::min(g[u][0]+h[a][0][x]+h[b][0][y]-f[u][0],g[u][1]+h[a][1][x]+h[b][1][y]-f[u][1]);
else{
if(u!=a) std::swap(a,b),std::swap(x,y);
ans[id]=g[a][x]+h[b][x][y];
}
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=Read(); q=Read(); scanf(" %s",s+1);
for(LL i=1;i<=n;++i) p[i]=Read();
for(LL i=1;i<n;++i){
LL u(Read()),v(Read()); Add(u,v); Add(v,u);
}
Dfs1(1,0); Dfs2(1,0);
for(LL i=1;i<=q;++i){
LL a(Read()),x(Read()),b(Read()),y(Read());
Qy[a].push_back((qy){a,x,b,y,i}); Qy[b].push_back((qy){a,x,b,y,i});
}
for(LL i=1;i<=n;++i) fa[i]=i;
Dfs3(1,0);
for(LL i=1;i<=q;++i){
if(ans[i]>=inf) puts("-1");
else printf("%lld\n",ans[i]);
}
return 0;
}
P5024 保卫王国(动态dp/整体dp/倍增dp)的更多相关文章
- P5024 保卫王国[倍增+dp]
窝当然不会ddp啦,要写这题当然是考虑优化裸dp啦,但是这题非常麻烦,于是变成了黑题. 首先,这个是没有上司的舞会模型,求图的带权最大独立集. 不考虑国王的限制条件,有 \[ dp[x][0]+=dp ...
- [倍增][换根DP]luogu P5024 保卫王国
题面 https://www.luogu.com.cn/problem/P5024 分析 可以对有限制的点对之间的链进行在倍增上的DP数组合并. 需要通过一次正向树形DP和一次换根DP得到g[0][i ...
- luogu5024 [NOIp2018]保卫王国 (动态dp)
可以直接套动态dp,但因为它询问之间相互独立,所以可以直接倍增记x转移到fa[x]的矩阵 #include<bits/stdc++.h> #define CLR(a,x) memset(a ...
- JZOJ5966. [NOIP2018TGD2T3] 保卫王国 (动态DP做法)
题目大意 这还不是人尽皆知? 有一棵树, 每个节点放军队的代价是\(a_i\), 一条边连接的两个点至少有一个要放军队, 还有\(q\)次询问, 每次规定其中的两个一定需要/不可放置军队, 问这样修改 ...
- 【NOIP2018】保卫王国 动态dp
此题场上打了一个正确的$44pts$,接着看错题疯狂$rush$“正确”的$44pts$,后来没$rush$完没将之前的代码$copy$回去,直接变零分了..... 这一题我们显然有一种$O(nm)$ ...
- luoguP5024 保卫王国 动态dp
题目大意: emmmmm 题解: QAQ #include <cstdio> #include <cstring> #include <iostream> usin ...
- LuoguP5024 保卫王国(动态DP,LCT)
最小权覆盖集 = 全集 - 最大权独立集 强制取点.不取点可以使用把权值改成正无穷或负无穷实现 接下来就是经典的"动态最大权独立集"了 O(nlogn). 这不是我说的,是immo ...
- BZOJ 5466: [Noip2018]保卫王国 动态DP
Code: // luogu-judger-enable-o2 #include<bits/stdc++.h> #define ll long long #define lson (now ...
- 【洛谷】P5024 保卫王国 (倍增)
前言 传送门 很多人写了题解了,我就懒得写了,推荐一篇博客 那就分享一下我的理解吧(说得好像有人看一样 对于每个点都只有选与不选两种情况,所以直接用倍增预处理出来两种情况的子树之内,子树之外的最值,最 ...
随机推荐
- Mybatis中三种关联关系的实现
目录 一对一查询 一对多查询 自关联查询: 多对多查询 总结: 三种关联关系:一对多,一对一,多对多 两种查询方式:嵌套查询,连接查询(也可称作:多表单独查询,多表连接查询) 每一种关联关系都可以通过 ...
- Wenaox 一款轻量性能好的微信小程序状态管理库
感慨一下!!! 从开始开发 wenaox 从开始到现在,,时不时更新一下,改一改 bug,却发现已经快 1 年了 orz 虽然很少人用 hhh,但偶尔也会有人提一些问题,我就知道还有人用的~ 感兴趣的 ...
- 【雅思】【绿宝书错词本】List13~24
List 13 ❤audacious a.大胆的:有冒险精神的:鲁莽的:厚颜无耻的 ❤tramp v.跋涉:踩踏 n.长途跋涉 ❤lexicographer n.词典编纂者 ❤manipulate v ...
- iview Carousel 轮播图自适应宽高;iview 轮播图 图片重叠问题;iview tabs 高度互相影响问题;vue this问题;
最终效果图: 一.轮播图中图片自适应宽高: <Carousel loop v-bind:height="imgHeight+'px'" v-model="caro ...
- 191010 python3分数划分ABC等级
# 题目:利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,# 60-89分之间的用B表示,60分以下的用C表示. while True: score = input(" ...
- 那些可作为GC Roots的对象
原文:https://blog.csdn.net/u010798968/article/details/72835255 一.名词解释 根搜索算法是JVM用来的判断对象是否存活的算法,此算法基本思路为 ...
- 51nod 2486 小b和矩阵
小b有一个m行n列的矩阵. 她会从(1,1)开始,顺时针螺旋访问该矩阵,每个元素恰好被访问一次. 请你按小b的访问顺序输出每个元素. 收起 输入 第一行输入两个数m和n,其中0<m,n≤50 ...
- 《3+1团队》【Beta】Scrum meeting 1
项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 3+1团队 团队博客地址 https://home.cnblogs.com/u/3-1group ...
- [Apio2010]patrol 巡逻
1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 2541 Solved: 1288[Submit][S ...
- Burp Suite的代理Brup Proxy的使用详解
Burp Proxy 是Burp Suite以用户驱动测试流程功能的核心,通过代理模式,可以让我们拦截.查看.修改所有在客户端和服务端之间传输的数据.