题目链接:

https://jzoj.net/senior/#contest/show/2529/2

题目:

题目背景:
尊者神高达很穷,所以他需要跑商来赚钱
题目描述:
基三的地图可以看做 n 个城市,m 条边的无向图,尊者神高达会从任意一个点出发并在起点购买货物,在旅途中任意一点卖出并最终到达终点,尊者神高达的时间很宝贵,所以他不会重复经过同一个城市,但是为了挣钱,他可能会去绕路。当然,由于工作室泛滥,所以一个城市的货物价格可能会发生改变。但是尊者神高达智商不足,他可能在一个很蠢的节点把货物卖掉,所以尊者神高达想知道每一次跑商最多能赔多少钱。

题目大意:

一张无向图,询问两点之间的可能的路径上的最小点权,带修改

前置知识点:

圆方树相关内容


首先我们看圆方树

圆方树连边规则:

如果一条边在仙人掌中不属于任何一个环中,那么它直接圆方树中的两个圆点。

对于仙人掌中的任意一个环,则每个环上的点在圆方树上对应的圆点向这个环对应的方点连边。如下图所示

注意圆方树只适用于仙人掌

怎么构造圆方树呢?

代码实现大概是这样的

void tarjan(int x,int pre)
{
dfn[x]=low[x]=++js;sta[++tp]=x;
for (int i=h1[x];i;i=e1[i].nxt)
{
if (i==(pre^)) continue;
int y=e1[i].to;
if (!dfn[y])
{
tarjan(y,i);
low[x]=min(low[x],low[y]);
if (low[y]>dfn[x]) link2(x,y),tp--;//找到桥
if (low[y]==dfn[x])//x在边双里
{
for (++nn,link2(x,nn);sta[tp]!=x;tp--) A[nn].insert(a[sta[tp]]),link2(nn,sta[tp]);
}
}
else low[x]=min(low[x],dfn[y]);
}
}

那么广义圆方树呢?

构造方法和圆方树区别不大,就是强制把两点一线也看成一个点双,建方点即可。

广义圆方树可以适用于无向图,而不只是仙人掌


题解:

言归正传,这道题我们怎么做?

如果没有修改的话,很显然,每个方点的值为这个点双中权值最小的点。 这样,就变成了裸的求树上两点路径上最小值,剖一下就行了

考虑这样怎么修改,记录下每一个圆点连接的方点,开个set维护一下每个方点连接的原点。对于修改的原点一个个修改每个与它相连的方点,但如果这个点是一个割点(连接很多个方点),那么可能就需要修改很多次,时间复杂度就不对了

题解用了更优秀的做法

把圆方树变成一个有根树,方点的值改为记录环上除环的根外权值最小的点,这样每个圆点只有一个对应的方点。如果 LCA 是方点,只需特判一下LCA的父亲。画个图理解吧

然而代码里写的广义圆方树用的是圆方树的构造方法,身为蒟蒻的我不知道两种构造方法有什么区别...这是我第一次写圆方树

还有与圆方树类似的做法,就是对每一个边双维护一个set,每次修改就是修改这个点所在的边双即可

注意边双和点双都意味着其中的任意两个点可以经过任意一个想经过的点到达彼此

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std; const int N=2e5+;
const int inf=1e9;
int n,m,tot1=,tot2=,js,tp,nn;
int a[N],h1[N],h2[N],dfn[N],low[N],sta[N],siz[N],dd[N],id[N],dep[N],son[N],top[N],pf[N];
int mi[N<<];
multiset <int> A[N];
struct EDGE
{
int to,nxt;
}e1[N<<],e2[N<<];
inline int read()
{
char ch=getchar();
int s=,f=;
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') {s=(s<<)+(s<<)+ch-'';ch=getchar();}
return s*f;
}
void link1(int u,int v)
{
e1[++tot1]=(EDGE){v,h1[u]};
h1[u]=tot1;
}
void link2(int u,int v)
{
pf[v]=u;
e2[++tot2]=(EDGE){v,h2[u]};
h2[u]=tot2;
}
void tarjan(int x,int pre)//构造园方树
{
dfn[x]=low[x]=++js;sta[++tp]=x;
for (int i=h1[x];i;i=e1[i].nxt)
{
if (i==(pre^)) continue;
int y=e1[i].to;
if (!dfn[y])
{
tarjan(y,i);
low[x]=min(low[x],low[y]);
if (low[y]>dfn[x]) link2(x,y),tp--;//找到桥
if (low[y]==dfn[x])//x在点双里
{
for (++nn,link2(x,nn);sta[tp]!=x;tp--) A[nn].insert(a[sta[tp]]),link2(nn,sta[tp]);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
void dfs1(int x)
{
siz[x]=;
for (int i=h2[x];i;i=e2[i].nxt)
{
int y=e2[i].to;
if (y==pf[x]) continue;
dep[y]=dep[x]+;
dfs1(y);
siz[x]+=siz[y];
if (!son[x]||siz[y]>siz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int tt)
{
dd[x]=++js;id[js]=x;
top[x]=tt;
if (!son[x]) return;
dfs2(son[x],tt);
for (int i=h2[x];i;i=e2[i].nxt)
{
int y=e2[i].to;
if (y==pf[x]||y==son[x]) continue;
dfs2(y,y);
}
}
#define mid ((l+r)>>1)
void pushup(int o) {mi[o]=min(mi[o<<],mi[o<<|]);}
void build(int o,int l,int r)
{
if (l==r) {mi[o]=a[id[l]];return;}
build(o<<,l,mid);build(o<<|,mid+,r);
pushup(o);
}
void update(int o,int l,int r,int x,int y)
{
if (l==r) {mi[o]=y;return;}
if (x<=mid) update(o<<,l,mid,x,y);
else update(o<<|,mid+,r,x,y);
pushup(o);
}
int query(int o,int l,int r,int x,int y)
{
if (l>=x&&r<=y) return mi[o];
int re=inf;
if (x<=mid) re=min(re,query(o<<,l,mid,x,y));
if (y>mid) re=min(re,query(o<<|,mid+,r,x,y));
return re;
}
int query_min(int x,int y)
{
int re=inf;
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
re=min(re,query(,,nn,dd[top[x]],dd[x]));
x=pf[top[x]];
}
if (dep[x]<dep[y]) swap(x,y);
re=min(re,query(,,nn,dd[y],dd[x]));
if (y>n&&pf[y]) re=min(re,a[pf[y]]);//特判LCA是否是方点,如果是的话还需要考虑它的父亲
return re;
}
int main()
{
freopen("paoshang.in","r",stdin);
freopen("paoshang.out","w",stdout);
n=read();m=read();nn=n;
for (int i=;i<=n;i++) a[i]=read();
for (int i=,u,v;i<=m;i++)
{
u=read();v=read();
link1(u,v);link1(v,u);
}
js=;tarjan(,-);
for (int i=n+;i<=nn;i++) a[i]=*A[i].begin();
dep[]=;dfs1();
js=;dfs2(,);
build(,,nn);
int q=read(),pr,pl;
char op[];
while (q--)
{
scanf("%s",op);
if (op[]=='C')
{
int x=read(),y=read();
update(,,nn,dd[x],y);
if ((pr=pf[x])>n)
{
A[pr].erase(A[pr].find(a[x]));A[pr].insert(y);
if (a[pr]!=(pl=*A[pr].begin())) update(,,nn,dd[pr],pl),a[pr]=pl;
}
a[x]=y;
}
if (op[]=='Q')
{
int x=read(),y=read();
printf("%d\n",a[x]-query_min(x,y));
}
}
return ;
}

[JZOJ 5909] [NOIP2018模拟10.16] 跑商(paoshang) 解题报告 (圆方树)的更多相关文章

  1. [jzoj 5930] [NOIP2018模拟10.26】山花 解题报告 (质因数分类)

    题目链接: http://172.16.0.132/senior/#contest/show/2538/2 题目: 小S决定从某一个节点$u$开始对其子树中与$u$距离小于$K$的节点代表的花树进行采 ...

  2. [JZOJ 5893] [NOIP2018模拟10.4] 括号序列 解题报告 (Hash+栈+map)

    题目链接: https://jzoj.net/senior/#main/show/5893 题目: 题解: 考虑暴力怎么做,我们枚举左端点,维护一个栈,依次加入元素,与栈顶元素和栈内第二个元素相同时弹 ...

  3. [JZOJ 5905] [NOIP2018模拟10.15] 黑暗之魂(darksoul) 解题报告 (拓扑排序+单调队列+无向图基环树)

    题目链接: http://172.16.0.132/senior/#main/show/5905 题目: oi_juruo热爱一款名叫黑暗之魂的游戏.在这个游戏中玩家要操纵一名有 点生命值的无火的余灰 ...

  4. [JZOJ 5908] [NOIP2018模拟10.16] 开荒(kaihuang)解题报告 (树状数组+思维)

    题目链接: https://jzoj.net/senior/#contest/show/2529/1 题目: 题目背景:尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门.他加入师门 ...

  5. [JZOJ 5888] [NOIP2018模拟9.29] GCD生成树 解题报告 (最大生成树+公约数)

    题目链接: http://172.16.0.132/senior/#main/show/5888 题目: 题解: 思路是这样的:两个数的最大公约数一定不会比这两个数的任意一个数大.因此我们把权值相等的 ...

  6. [JZOJ 5885] [NOIP2018模拟9.27] 物理实验 解题报告 (思维)

    题目链接: https://jzoj.net/senior/#main/show/5885 题目: 题解: 把$a$数组按升序排序 我们可以枚举$x$,发现对于任意$x$,最优情况下$y$一定等于$x ...

  7. [jzoj 5926] [NOIP2018模拟10.25] naive 的图 解题报告(kruskal重构树+二维数点)

    题目链接: https://jzoj.net/senior/#main/show/5926 题目: 题解: 显然最小的最大路径在最小生成树上(最小生成树=最小瓶颈生成树) 于是我们建出kruskal重 ...

  8. [JZOJ 5911] [NOIP2018模拟10.18] Travel 解题报告 (期望+树形DP)

    题目链接: http://172.16.0.132/senior/#contest/show/2530/1 题目: EZ同学家里非常富有,但又极其的谦虚,说话又好听,是个不可多得的人才.        ...

  9. [JZOJ 5912] [NOIP2018模拟10.18] VanUSee 解题报告 (KMP+博弈)

    题目链接: https://jzoj.net/senior/#contest/show/2530/2 题目: 众所周知,cqf童鞋对哲学有着深入的理解和认识,并常常将哲学思想应用在实际生活中,例如锻炼 ...

随机推荐

  1. nyoj--114--某种序列(滚动数组)

    某种序列 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 数列A满足An = An-1 + An-2 + An-3, n >= 3  编写程序,给定A0, A1 和 ...

  2. Regexp-Utils:身份证号校验

    ylbtech-Regexp-Utils:身份证号校验 1.返回顶部 1.方法 var idCardNoUtil = { /*省,直辖市代码表*/ provinceAndCitys: { 11: &q ...

  3. checkbox复选框和div click事件重叠,点击div后复选框也被选中,同时改变div颜色,否则则不选中

     checkbox复选框和div click事件重叠,点击div后复选框也被选中,同时改变div颜色,否则则不选中 <!DOCTYPE html> <html lang=" ...

  4. 如何用写js弹出层 ----2017-03-29

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. jsonp模仿了得一个百度搜索框

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. UVa 424 Integer Inquiry 【大数相加】

    解题思路:因为给定的数据是多组,所以我们只需要多次做加法就可以了,将上一次的和又作为下一次加法运算的一个加数. 反思:还是题意理解不够清楚,最开始以为只是算三个大数相加,后来才发现是多个,然后注意到当 ...

  7. Python更换pip源,更换conda源

    更换pip源: 1.在windows文件管理器中,输入 %APPDATA% 2.在该目录下新建pip文件夹,然后到pip文件夹里面去新建个pip.ini文件 3.在新建的pip.ini文件中输入以下内 ...

  8. webpack——bable-loader,core,preset,编译es6

    //文件夹,在命令行中npm init 之后可以一直回车,答y 出现文件 然后安装bable npm install -save-dev babel-loader babel-core 文件 然后我们 ...

  9. Python笔记25-----------创建二维列表【浅copy】和转置

    一.创建二维列表 1.二维列表创建第二维的时候,如果采用*2这种方式,这是一种浅复制的方式,同时引用到同一个list,如上图的C. 这种形式,不方便修改C[ i ][ j ]的数据,如果改C[ 0 ] ...

  10. 一些AngularJs

    # AngularJs部分 #     详情可参考文档----依赖注入--不是主动地获取而是被动的接收,需要什么就要什么,这样灵活较高,如:$scope ----指令--内部:ng-    如:ng- ...