题目:https://www.luogu.org/problemnew/show/P5024

考场上只会写n,m<=2000的暴力,还想了想A1和A2的情况,不过好像只得了A1的分。然后仔细一看,原来是把dp2[ ][ ]写成dp[ ][ ]了。改一下,就能得到A1和A2的分。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+;
const ll INF=1e10+;
int n,m,p[N],hd[N],xnt,to[N<<],nxt[N<<];
int q0,q1,f0,f1;
ll dp[N][],dp2[N][];
char ch[];
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return fx?ret:-ret;
}
void add(int x,int y)
{
to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt;
}
ll Mn(ll a,ll b){return a<b?a:b;}
ll Mx(ll a,ll b){return a>b?a:b;}
void dfs(int cr,int fa)
{
dp[cr][]=; dp[cr][]=p[cr];
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs(v,cr);
dp[cr][]+=dp[v][];
dp[cr][]+=Mn(dp[v][],dp[v][]);
}
if(cr==q0)dp[cr][!f0]=INF;
if(cr==q1)dp[cr][!f1]=INF;
}
bool chk()
{
bool flag=;
for(int i=hd[q0];i;i=nxt[i])
if(to[i]==q1){flag=;break;}
if(flag&&!f0&&!f1)
{
puts("-1");return true;
}
return false;
}
void solve1()
{
for(int i=;i<=m;i++)
{
q0=rdn();f0=rdn();q1=rdn();f1=rdn();
if(chk())continue;
dfs(,);
printf("%lld\n",Mn(dp[][],dp[][]));
}
}
void solve2()
{
dp[][]=p[]; dp[][]=INF;
for(int i=;i<=n;i++)
{
dp[i][]=Mn(dp[i-][],dp[i-][])+p[i];
dp[i][]=dp[i-][];
}
dp2[n][]=p[n]; dp2[n][]=;
for(int i=n-;i;i--)
{
dp2[i][]=Mn(dp2[i+][],dp2[i+][])+p[i];
dp2[i][]=dp2[i+][];
}
for(int i=;i<=m;i++)
{
q0=rdn();f0=rdn();q1=rdn();f1=rdn();
if(chk())continue;
printf("%lld\n",dp[q1][f1]+dp2[q1][f1]-(f1?p[q1]:));
}
}
void solve3()
{
dp[][]=p[]; dp[][]=;
for(int i=;i<=n;i++)
{
dp[i][]=Mn(dp[i-][],dp[i-][])+p[i];
dp[i][]=dp[i-][];
}
dp2[n][]=p[n]; dp2[n][]=;
for(int i=n-;i;i--)
{
dp[i][]=Mn(dp[i+][],dp[i+][])+p[i];
dp[i][]=dp[i+][];
}
for(int i=;i<=m;i++)
{
q0=rdn();f0=rdn();q1=rdn();f1=rdn();
if(chk())continue;
if(q0>q1)swap(q0,q1),swap(f0,f1);
printf("%lld\n",dp[q0][f0]+dp2[q1][f1]);
}
}
int main()
{
freopen("defense.in","r",stdin);
freopen("defense.out","w",stdout);
n=rdn();m=rdn();scanf("%s",ch+);
for(int i=;i<=n;i++)p[i]=rdn();
for(int i=,u,v;i<n;i++)
{
u=rdn(); v=rdn(); add(u,v); add(v,u);
}
if(n<=)solve1();
else if(ch[]=='A'&&ch[]=='')solve2();
else if(ch[]=='A'&&ch[]=='')solve3();
else solve1();
return ;
}

然后得知A的分好像就是一个线段树。想一想,就记录一下该区间两端的是0还是1就行了。

正解的一种是倍增。

  先做出正常的dp[ ][ 0/1 ]数组。考虑倍增,f[ cr ][ i ][0/1][0/1]表示自己到第 i 个祖先的路上的贡献(不含自己及自己子树,含祖先,含路上的点以及它们的分叉,不含祖先上面的部分);只要把dp数组累加起来就行了;累加的时候注意把自己这一条减去,就是如果用父亲的dp[ ][1]的话,就减去自己的min(dp[ ][0],dp[ ][1]),不然就减去自己的dp[ ][1],然后把父亲的这个减去之后的东西放到f[ ][0][ ][ ]里;i 的其他值正常倍增合并就行了。

  考虑统计,两个端点就用它们的dp值就行;路上的就倍增地走,用 f 值就行;lca处需要一个以该点为根的值,减去走来的那两条,然后累加到答案里。因为在lca处的那两条一定是原树的两个子树,所以可以换一遍根记录每个点作为根的值,用的时候减去那两条的dp值即可。

  思路似乎也很简单?倍增真是好物。考虑两个点的问题时应该想一想倍增、lca之类的。然后考虑倍增数组维护什么,想一想统计答案的时候分为几种不同的部分,应该就差不多了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+,M=; const ll INF=1e10+;
int n,m,p[N],pr[N][M],hd[N],xnt,to[N<<],nxt[N<<],lm,dep[N];
ll dp[N][],info[N][],f[N][M][][];
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='') ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return fx?ret:-ret;
}
ll Mn(ll a,ll b){return a<b?a:b;}
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void dfs1(int cr,int fa)
{
dp[cr][]=p[cr]; dep[cr]=dep[fa]+;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs1(v,cr);
dp[cr][]+=Mn(dp[v][],dp[v][]);
dp[cr][]+=dp[v][];
}
}
void dfs2(int cr,int fa,ll w0,ll w1)
{
info[cr][]=dp[cr][]+w1;
info[cr][]=dp[cr][]+Mn(w0,w1);
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa)
{
dfs2(v,cr,info[cr][]-dp[v][],info[cr][]-Mn(dp[v][],dp[v][]));
}
}
void dfsx(int cr,int fa)
{
for(int i=,d;i<=lm&&pr[pr[cr][i-]][i-];i++)
{
d=pr[cr][i-];
pr[cr][i]=pr[d][i-];
for(int j=;j<=;j++)
for(int k=;k<=;k++)
{
f[cr][i][j][k]=Mn(f[cr][i-][j][]+f[d][i-][][k],f[cr][i-][j][]+f[d][i-][][k]);//not dec p[d]
}
}
for(int i=hd[cr],v;i;i=nxt[i])//f:without son
if((v=to[i])!=fa)
{
pr[v][]=cr;
for(int j=;j<=;j++)
{
f[v][][j][]=dp[cr][]-dp[v][];//not pls p[v]
f[v][][j][]=dp[cr][]-Mn(dp[v][],dp[v][]);//not pls p[v]
}
f[v][][][]=INF;
dfsx(v,cr);
}
}
ll cz(int x,int f0,int y,int f1)
{
ll d00=,d01=,d10=,d11=;
if(dep[x]<dep[y])swap(x,y),swap(f0,f1);
int x0=x,y0=y;
ll y00=,y01=;
if(f0)y00=INF; else y01=INF;
for(int i=lm;i>=;i--)
if(dep[pr[x][i]]>dep[y])//> not >=
{
d00=Mn(y00+f[x][i][][],y01+f[x][i][][]);
d01=Mn(y00+f[x][i][][],y01+f[x][i][][]);
y00=d00; y01=d01;
x=pr[x][i];
}
ll ret=;
if(pr[x][]==y)
{
ret=info[y][f1];
if(f1) ret-=Mn(dp[x][],dp[x][]),ret+=Mn(y00,y01);//y** not d** for init
else ret-=dp[x][],ret+=y01;
ret+=dp[x0][f0];
return ret;
}
if(dep[x]!=dep[y])
{
d00=Mn(y00+f[x][][][],y01+f[x][][][]);
d01=Mn(y00+f[x][][][],y01+f[x][][][]);
y00=d00; y01=d01;
x=pr[x][];
} ll y10=,y11=;
if(f1)y10=INF; else y11=INF;
for(int i=lm;i>=;i--)
if(pr[x][i]!=pr[y][i])
{
d00=Mn(y00+f[x][i][][],y01+f[x][i][][]);
d01=Mn(y00+f[x][i][][],y01+f[x][i][][]);
y00=d00; y01=d01; x=pr[x][i];
d10=Mn(y10+f[y][i][][],y11+f[y][i][][]);
d11=Mn(y10+f[y][i][][],y11+f[y][i][][]);
y10=d10; y11=d11; y=pr[y][i];
}
int d=pr[x][];
ret=info[d][]-Mn(dp[x][],dp[x][])-Mn(dp[y][],dp[y][])+Mn(y00,y01)+Mn(y10,y11);//y** not d** for init
ll rt2=info[d][]-dp[x][]-dp[y][]+y01+y11;
ret=Mn(ret,rt2)+dp[x0][f0]+dp[y0][f1];
return ret;
}
int main()
{
freopen("defense.in","r",stdin);
freopen("defense.out","w",stdout);
char ch[];
n=rdn(); m=rdn(); scanf("%s",ch);
for(;(<<lm)<n;lm++);
for(int i=;i<=n;i++)p[i]=rdn();
for(int i=,u,v;i<n;i++)
{
u=rdn(); v=rdn(); add(u,v); add(v,u);
}
dfs1(,);dfs2(,,,);dfsx(,);
for(int i=,a,b,x,y;i<=m;i++)
{
a=rdn();x=rdn();b=rdn();y=rdn();
ll ans=cz(a,x,b,y);
printf("%lld\n",ans>=INF?-:ans);
}
return ;
}

NOIp2018 D2T3 defense——树上倍增的更多相关文章

  1. Codevs 2370 小机房的树 LCA 树上倍增

    题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...

  2. NOIP2013 货车运输 (最大生成树+树上倍增LCA)

    死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...

  3. HDU 4822 Tri-war(LCA树上倍增)(2013 Asia Regional Changchun)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4822 Problem Description Three countries, Red, Yellow ...

  4. [NOIP2013/Codevs3287]货车运输-最小[大]生成树-树上倍增

    Problem 树上倍增 题目大意 给出一个图,给出若干个点对u,v,求u,v的一条路径,该路径上最小的边权值最大. Solution 看到这个题第一反应是图论.. 然而,任意路径最小的边权值最大,如 ...

  5. 树上倍增求LCA及例题

    先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...

  6. [树上倍增+二分答案][NOIP2012]运输计划

    题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 公元 2044 年,人类进入了宇宙纪元 L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n− ...

  7. 两种lca的求法:树上倍增,tarjan

    第一种:树上倍增 f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点. 初始条件:f[x][0]=fa[x] 递推式:f[x][k]=f[ f[x][k-1] ][k-1] 一次bfs ...

  8. 【NOIP2013/Codevs3287】货车运输-最小生成树(大)-树上倍增

    https://www.luogu.org/problemnew/show/P1967 由题可知,我们走的路的边应尽可能大,所以通过kruscal建最大生成树的图,再树上倍增,注意可能有多棵树; #i ...

  9. LCA树上倍增

    LCA就是最近公共祖先,比如 节点10和11的LCA就是8,9和3的LCA就是3. 我们这里讲一下用树上倍增来求LCA. 大家都可以写出暴力解法,两个节点依次一步一步往上爬,直到爬到了相同的一个节点. ...

随机推荐

  1. POJ 1442 优先队列

    题意:有一些ADD和GET操作.n次ADD操作,每次往序列中加入一个数,由ADD操作可知序列长度为1-n时序列的组成.GET操作输入一个序列长度,输出当前长度序列第i大的元素的值.i初始为0,每次GE ...

  2. HDU 1337 && POJ 1218&& zju 1350 方法总结

    题目链接http://acm.hdu.edu.cn/showproblem.php?pid=1337 杭电 http://poj.org/problem?id=1218清华 http://acm.zj ...

  3. hdu 5777 domino 贪心

    domino Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Problem ...

  4. 前端分布引导插件IntroJs的使用

    在用户第一次使用网站的时候,一般会提供新手引导的提示,提示用户重要的功能使用,实现方法比较多,但是有一点,屏幕的自适应问题,大多数自己写的实现方法无非就是一个div遮罩层,然后再需要指引的位置放置一张 ...

  5. 通过application.properties配置SpringBoot项目

    application.properties可以自己新建,放在这里:(该文件可以放在4个地方,详情百度) 在文件中添加:file_path=E://Tools//apache-tomcat-9.0.1 ...

  6. C++(十六) — 类中引用成员函数、命名空间的使用

    1.为什么类中引用成员函数? 类将属性和方法做了封装.类是一种数据类型,也就是:固定大小内存块的别名. 类的定义是一个抽象的概念,定义时不分配内存,当用类定义对象时,才分配一个固定大小的内存块. 此时 ...

  7. 《Advanced Bash-scripting Guide》学习(一):对一个增强和广义的删除logfile的脚本的理解

    本文所选的例子来自于<Advanced Bash-scripting Gudie>一书,译者 杨春敏 黄毅 cleanup:一个增强和广义的删除logfile的脚本 #!/bin/bash ...

  8. Android进阶常用网站

    Android进阶常用网站 android中文网 Android Studio 安卓开发者社区

  9. opencv:鼠标操作

    示例程序: #include <opencv.hpp> using namespace cv; #define WINDOW_NAME "程序窗口" // ------ ...

  10. 【费用流】bzoj1221 [HNOI2001] 软件开发

    几乎为“线性规划与网络流24题”中的餐巾问题. 这里把S看成毛巾的来源,T看成软件公司,我们的目的就是让每天的毛巾满足要求(边满流). 引用题解: [问题分析] 网络优化问题,用最小费用最大流解决. ...