[JZOJ 5909] [NOIP2018模拟10.16] 跑商(paoshang) 解题报告 (圆方树)
题目链接:
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) 解题报告 (圆方树)的更多相关文章
- [jzoj 5930] [NOIP2018模拟10.26】山花 解题报告 (质因数分类)
题目链接: http://172.16.0.132/senior/#contest/show/2538/2 题目: 小S决定从某一个节点$u$开始对其子树中与$u$距离小于$K$的节点代表的花树进行采 ...
- [JZOJ 5893] [NOIP2018模拟10.4] 括号序列 解题报告 (Hash+栈+map)
题目链接: https://jzoj.net/senior/#main/show/5893 题目: 题解: 考虑暴力怎么做,我们枚举左端点,维护一个栈,依次加入元素,与栈顶元素和栈内第二个元素相同时弹 ...
- [JZOJ 5905] [NOIP2018模拟10.15] 黑暗之魂(darksoul) 解题报告 (拓扑排序+单调队列+无向图基环树)
题目链接: http://172.16.0.132/senior/#main/show/5905 题目: oi_juruo热爱一款名叫黑暗之魂的游戏.在这个游戏中玩家要操纵一名有 点生命值的无火的余灰 ...
- [JZOJ 5908] [NOIP2018模拟10.16] 开荒(kaihuang)解题报告 (树状数组+思维)
题目链接: https://jzoj.net/senior/#contest/show/2529/1 题目: 题目背景:尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门.他加入师门 ...
- [JZOJ 5888] [NOIP2018模拟9.29] GCD生成树 解题报告 (最大生成树+公约数)
题目链接: http://172.16.0.132/senior/#main/show/5888 题目: 题解: 思路是这样的:两个数的最大公约数一定不会比这两个数的任意一个数大.因此我们把权值相等的 ...
- [JZOJ 5885] [NOIP2018模拟9.27] 物理实验 解题报告 (思维)
题目链接: https://jzoj.net/senior/#main/show/5885 题目: 题解: 把$a$数组按升序排序 我们可以枚举$x$,发现对于任意$x$,最优情况下$y$一定等于$x ...
- [jzoj 5926] [NOIP2018模拟10.25] naive 的图 解题报告(kruskal重构树+二维数点)
题目链接: https://jzoj.net/senior/#main/show/5926 题目: 题解: 显然最小的最大路径在最小生成树上(最小生成树=最小瓶颈生成树) 于是我们建出kruskal重 ...
- [JZOJ 5911] [NOIP2018模拟10.18] Travel 解题报告 (期望+树形DP)
题目链接: http://172.16.0.132/senior/#contest/show/2530/1 题目: EZ同学家里非常富有,但又极其的谦虚,说话又好听,是个不可多得的人才. ...
- [JZOJ 5912] [NOIP2018模拟10.18] VanUSee 解题报告 (KMP+博弈)
题目链接: https://jzoj.net/senior/#contest/show/2530/2 题目: 众所周知,cqf童鞋对哲学有着深入的理解和认识,并常常将哲学思想应用在实际生活中,例如锻炼 ...
随机推荐
- 洛谷p3803 FFT入门
洛谷p3803 FFT入门 ps:花了我一天的时间弄懂fft的原理,感觉fft的折半很神奇! 大致谈一谈FFT的基本原理: 对于两个多项式的卷积,可以O(n^2)求出来(妥妥的暴力) 显然一个多项式可 ...
- When Cyber Security Meets Machine Learning 机器学习 安全分析 对于安全领域的总结很有用 看未来演进方向
链接:http://ucys.ugr.es/jnic2016/docs/MachineLearning_LiorRokachJNIC2016.pdf https://people.eecs.berke ...
- OC中的类扩展
类扩展 是在原有类的基础扩展一个新的属性和对象方法 但是方法的实现还是要写在原有的声明中,不然是不会被访问到的 类扩展可以扩展在新的头文件中,然后在主函数中导入. 利用类扩展可以变相的实现属性的私有化 ...
- POJ 1155 树形DP
题意:电视台发送信号给很多用户,每个用户有愿意出的钱,电视台经过的路线都有一定费用,求电视台不损失的情况下最多给多少用户发送信号. 转自:http://www.cnblogs.com/andre050 ...
- MFC框架下Opengl窗口闪屏问题解决方案
转自https://blog.csdn.net/niusiqiang/article/details/43116153 虽然启用了双缓冲,但是仍然会出闪屏的情况,这是由于OpenGL自己有刷新背景的函 ...
- ZBrush中绘制层是什么意思?
我们经常使用笔刷雕刻模型,在使用笔刷为头部模型添加一些纹理效果时,有时可能会有不满意的地方,但是很难修改,也很难把它还原为原来的状态,这时我们就可以使用Layers(绘制层)来将雕刻的部分分到每一个层 ...
- 使用LayUI在页面实现加载层(图标)代码:
实现代码: var index = layer.load({ shade: [0.4,'#def'], icon :' 实现效果: 可以使用 layer.close(index); 来控制其在什么时 ...
- CF1042F Leaf Sets (贪心+树上构造)
题目大意:给你一棵树,让你对叶节点分组,保证每组中,任意两个叶节点之间的距离不大于K,求最小的组数 手动yy的贪心竟然对的 对于每个节点,维护一个$ma[i]$,表示在$i$节点的子树内 未被分组的叶 ...
- 基于LXC的虚拟网络自动部署
一.问题: 在搭建以LXC为基础的虚拟网络时,网络参数繁多,配置过程繁琐.面临一个新的网络拓扑结构时,通常要花费大量时间来构建网络.因此,如果能通过配置文件,自动生成相对应的网络拓扑,并生成操作指令. ...
- springboot的几个缓存相关注解
@Cacheable:查询 几个属性: cacheNames/value:指定缓存组件的名字: key:缓存数据使用的key,可以用来指定.默认即使用方法参数的值 keyGenerator ...