题目: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. shell printf命令:格式化输出语句

    printf 命令用于格式化输出, 是echo命令的增强版.它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同. 注意:printf 由 POSIX 标准所定义,移植性要比 ech ...

  2. 聊聊这两天在linux安装PHP7遇到的坑,真的是坑死人不偿命啊

    前情摘要: 这两天要在虚拟机上部署项目,用于测试在linux上项目效果怎样,然后这两天就一直在部署apache+mysql+php 其实部署还是很简单的具体的apache和mysql部署方法请看其他两 ...

  3. hadoop入门小知识点

    注意各个主机之间的通信 文件的复制 scp指令 scp /etc/profile acm03:/etc 所有历史版本: archive.apache.org hdfs://acm01:9000     ...

  4. eclipse中的错误解决——Servlet cannot be resolved to a type

    问题如图 解决问题方法

  5. Permutations,全排列

    问题描述:给定一个数组,数字中数字不重复,求所有全排列. 算法分析:可以用交换递归法,也可以用插入法. 递归法:例如,123,先把1和1交换,然后递归全排列2和3,然后再把1和1换回来.1和2交换,全 ...

  6. mssqlserver,mysql,oracle分页查询

    分页查询语句是sql语句编程中很长见的一个典型应用,用sql语句来分页比一些分页控件的速度要快,所以sql语句的分页在实际编程应用中还是非常广泛的. 今天给大家分享几条不同数据库编程用的分页查询语句. ...

  7. scala学习手记19 - Option类型

    看到Option类型就知道这本教材应该要说那个了. 使用过guava后,应该知道guava中的Optional类的作用是什么.算了找下原始文档好了: Optional<T> is a wa ...

  8. 计算机网络 - IP和端口

    计算机网络分层模型 OSI分层模型:物理层.数据链路层.网络层.传输层.会话层.表示层.应用层: TCP/IP分层模型:物理+数据链路层.网络层.传输层.应用层: IP地址 IP地址是一个32位的整数 ...

  9. npm install 时总是报phantomjs-prebuilt@2.1.14安装失败

    在npm install时总是报如下错误, 尝试单独安装:npm install phantomjs-prebuilt@2.1.14 还是报错 Please report this full log ...

  10. dom兼容性问题3 元素操作

    /* var oLi = document.createElement('li'); oUl.appendChild( oLi ); }; createElement('') : 创建一个dom元素 ...