博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
题解 P4949 【最短距离】
阅读量:4988 次
发布时间:2019-06-12

本文共 3501 字,大约阅读时间需要 11 分钟。

吼题啊

刚开始看上去又以为是LCT啥子的。

后来发现,TM是个图。

然后果断准备放弃,突然发现只有N个点N条边。

woc,这不就一个基环树上树链剖分吗。。。

关于基环树问题,相信大家都一定很有经验了吧,用个并查集找出多的边,然后把图分成一棵树和一条边,然后树上就树上做,多出来的边就可以另外处理。

关于边权转点权,直接在查询的最后减去LCA的点权就OK了,其他当成点权处理。

关于线段树,这个大家都会改吧。。。

关于修改操作,直接找出相应的点权,然后修改就行了,找点权记得先边的编号乘2

总之还是很简单的

下面可以看看代码注释:

#include
#define maxn 4000001#define L(x) (x<<1)#define R(x) ((x<<1)|1)using namespace std;int tree[maxn],tag[maxn];int rev[maxn],dep[maxn],size[maxn],seg[maxn],top[maxn],son[maxn],father[maxn];int n,m,root,x,y,z,a[maxn],visx[maxn],visy[maxn],tot,mode;int cnt,from[maxn],to[maxn],Next[maxn],head[maxn];int Gx,Gy,Gz,Gd;int fa[maxn],X[maxn],Y[maxn],Z[maxn];int find(int x){ if(fa[x]==x)return x; else return fa[x]=find(fa[x]);}bool merge(int x,int y){ int i=find(x),j=find(y); if(i==j)return false; fa[i]=j;return true;}void add(int x,int y){ cnt++; from[cnt]=x;to[cnt]=y; Next[cnt]=head[x];head[x]=cnt;}void update(int node,int begin,int end,int x,int y,int val){ if(begin>y||end
=x&&end<=y){ tree[node]=val; return; }else{ int mid=(begin+end)>>1; if(x<=mid)update(L(node),begin,mid,x,y,val); if(y>mid) update(R(node),mid+1,end,x,y,val); tree[node]=tree[L(node)]+tree[R(node)]; }}int query(int node,int begin,int end,int x,int y){ if(begin>=x&&end<=y){ return tree[node]; }else{ int mid=(begin+end)>>1,sum=0; if(x<=mid)sum+=query(L(node),begin,mid,x,y); if(y>mid) sum+=query(R(node),mid+1,end,x,y); return sum; }}int dfs1(int x){ size[x]=1; dep[x]=dep[father[x]]+1; for(int i=head[x];i!=-1;i=Next[i]){ int v=to[i],big=0; if(father[x]==v)continue; father[v]=x; big=dfs1(v); size[x]+=big; if(big>size[son[x]])son[x]=v; } return size[x]; }void dfs2(int x){ if(son[x]){ seg[son[x]]=++seg[0]; top[son[x]]=top[x]; rev[seg[0]]=son[x]; dfs2(son[x]); } for(int i=head[x];i!=-1;i=Next[i]){ int v=to[i]; if(!top[v]){ seg[v]=++seg[0]; top[v]=v; rev[seg[0]]=v; dfs2(v); } }}int linkquery(int x,int y){ int fx=top[x],fy=top[y],ans=0; while(fx!=fy){ if(dep[fx]
dep[y])swap(x,y); ans+=query(1,1,seg[0],seg[x],seg[y]); ans-=query(1,1,seg[0],seg[x],seg[x]); return ans;}void change(int x,int y){ //修改边权 x*=2; //记得乘2 x=dep[from[x]]>dep[to[x]]?from[x]:to[x]; //因为边权钦定的是深度大的点 update(1,1,seg[0],seg[x],seg[x],y); //直接改}int main(){ memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m);root=1; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=n;i++){ scanf("%d%d%d",&x,&y,&z); if(merge(x,y)){add(x,y),add(y,x);} else Gx=x,Gy=y,Gz=z,Gd=i,cnt+=2; //并查集判多的边 X[i]=x;Y[i]=y;Z[i]=z; } dfs1(root); seg[root]=++seg[0]; rev[seg[0]]=root; top[root]=root; dfs2(root); for(int i=1;i<=n;i++)if(Gd!=i)change(i,Z[i]); //因为本人懒得写build,所以这样写 for(int i=1;i<=m;i++){ scanf("%d%d%d",&mode,&x,&y); if(mode==1){ if(x==Gd)Gz=y; //如果是多余的边就直接改 else change(x,y); //不然就另外改 }else{ int ans; ans=linkquery(x,y); //第一种情况,不经过多余的边 ans=min(ans,Gz+min(linkquery(x,Gx)+linkquery(Gy,y),linkquery(x,Gy)+linkquery(Gx,y))); //第二种情况,经过多余的边 printf("%d\n",ans); } }}

转载于:https://www.cnblogs.com/hyfhaha/p/10678150.html

你可能感兴趣的文章
Spark报错:Failed to locate the winutils binary in the hadoop binary path
查看>>
结构体学习笔记9——结构体大小计算规则
查看>>
git删除远程仓库文件
查看>>
quartz定时任务时间设置
查看>>
PB与SQL SERVER连接
查看>>
使用Nexus搭建Maven私服
查看>>
css之display:inline-block与float区别(可以尝试用一下)
查看>>
ShopNc商城修改详情
查看>>
AngularJS 拦截器和应用例子(转)
查看>>
20180628
查看>>
CISCO 1841 升级ios
查看>>
iOS绘制虚线
查看>>
[CLR via C#]14. 字符、字符串和文本处理
查看>>
关于JDBC和连接池我学到的(转载保存)
查看>>
php 页面公共部分 转化为js document.write(); 并由匿名函数包裹
查看>>
javascript事件小结(事件处理程序方式)--javascript高级程序设计笔记
查看>>
WPF Visibility属性用法
查看>>
zoj 2334 Monkey King 左偏树+并查集
查看>>
删除博客园复制 python 代码时遗留的空格
查看>>
根据元素取两个list<T>不同
查看>>