日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDU - 6393 Traffic Network in Numazu(线段树+LCA+树链剖分+并查集)

發(fā)布時(shí)間:2024/4/11 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDU - 6393 Traffic Network in Numazu(线段树+LCA+树链剖分+并查集) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題目鏈接:點(diǎn)擊查看

題目大意:給出一個(gè)由n個(gè)點(diǎn)和n條邊組成的圖,每條邊都有權(quán)值,題目保證圖是連通的,然后給出m個(gè)詢問,每次詢問分為兩種形式:

  • 0 x y:將第x條邊的權(quán)值修改為y
  • 1 x y:查詢x-y這條路徑上的權(quán)值和
  • 題目分析:因?yàn)檫@個(gè)題目故意給的是n個(gè)點(diǎn)和n條邊,所以我們可以發(fā)現(xiàn)這一定是一個(gè)有環(huán)的圖,如果n個(gè)點(diǎn)和n-1條邊組成一棵樹的話會(huì)多出一條邊來,那么先讓他多出來不要管,至于怎么求這個(gè)多出來的邊,我們直接用并查集操作一下就行了,因?yàn)樯厦嬉蔡岬搅?#xff0c;多出來一條邊肯定會(huì)組成環(huán),所以多出來的那條邊會(huì)發(fā)現(xiàn)早就已經(jīng)加入到集合中去了,單獨(dú)拿出來就行

    針對(duì)這兩個(gè)操作我們需要分析一下,如果沒有第一個(gè)操作的話,那就是一個(gè)簡(jiǎn)單的樹上求前綴和的問題了,我們只需要特判一下多出來的那條邊即可,大體思路就是先用樹上倍增或樹鏈剖分跑一下lca,然后用樹上前綴和求一下u-lca(u,v)-v這條路徑上的權(quán)值和,但是我們?cè)撛趺刺嘏卸喑鰜淼哪菞l邊呢?

    假如讓我們求的是x到y(tǒng)這條路徑上的權(quán)值和,而多出來的那條邊的兩個(gè)頂點(diǎn)是u和v,權(quán)值為w,那么我們可以先跑一下下面三個(gè)值,求一下最小值就是答案了:(sum[i]代表根節(jié)點(diǎn)到i節(jié)點(diǎn)的前綴和)

  • sum[x]+sum[y]-2*sum[lca(x,y)]//不經(jīng)過多出來的那條邊:x-lca(x,y)-y
  • (sum[x]+sum[v]-2*sum[lca(x,v)])+(sum[y]+sum[u]-2*sum[lca(y,u)])+w//經(jīng)過多出來的那條邊:x-lca(x,v)-v-u-lca(u,y)-y
  • (sum[x]+sum[u]-2*sum[lca(x,u)])+(sum[y]+sum[v]-2*sum[lca(y,v)])+w//經(jīng)過多出來的那條邊:x-lca(x,u)-u-v-lca(v,y)-y
  • 但是!這個(gè)題目是需要修改的,在樹上看到修改無非就是往線段樹和樹狀數(shù)組上靠攏了,因?yàn)槲覍?duì)樹狀數(shù)組的理解不太夠,所以還是選擇比較好用的線段樹來維護(hù)吧,具體就是先用樹鏈剖分將整個(gè)樹剖一下,然后因?yàn)檫厵?quán)比較難處理,就將邊權(quán)映射到點(diǎn)權(quán)上,昨天剛做了一個(gè)類似的題目,于是就很好處理了,記著特別處理一下最后到根節(jié)點(diǎn)那條鏈上的關(guān)系就好,再一一對(duì)應(yīng)到線段樹上,就可以寫一個(gè)getnum函數(shù)用來以logn*logn的時(shí)間復(fù)雜度獲取任意一點(diǎn)到根節(jié)點(diǎn)的權(quán)值和了,也就是上面提到的前綴和,然后對(duì)于修改操作的話,如果是多出來的那條邊,我們直接修改就行,如果是樹上的邊,用線段樹logn修改一下就好了,這個(gè)題我感覺比較討厭的地方是關(guān)于兩端映射,有兩個(gè)地方需要用到映射,一個(gè)是樹剖打dfs序的時(shí)候需要將序號(hào)和節(jié)點(diǎn)映射一下,還有就是關(guān)于每一條邊和映射到的節(jié)點(diǎn)也需要對(duì)應(yīng)好,注意好這些細(xì)節(jié),寫代碼盡量多用函數(shù)包裝一下,這樣寫完后debug起來也比較輕松

    廢話不多說了,最后就是記得所有與權(quán)值相關(guān)的地方都開一下longlong,因?yàn)?e5*1e5就爆掉int了

    代碼:

    #include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;int n,m;struct Edge {int u,v,id;LL w;Edge(int U,int V,LL W,int ID){u=U;v=V;w=W;id=ID;}Edge(){} }edge[N];//存邊用vector<Edge>node[N];//鄰接表struct Node {int l,r;LL sum; }tree[N<<2];//線段樹用int rk[N];//邊權(quán)與點(diǎn)權(quán)的映射 rk[編號(hào)]=節(jié)點(diǎn)LL val[N];//邊權(quán)與點(diǎn)權(quán)的映射 val[節(jié)點(diǎn)]=權(quán)值int id[N],idd[N];//dfs序與節(jié)點(diǎn)的映射 id[節(jié)點(diǎn)]=dfs序 idd[dfs序]=節(jié)點(diǎn)void pushup(int k)//以下為簡(jiǎn)單線段樹維護(hù)sum和 {tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; }void build(int k,int l,int r) {tree[k].l=l;tree[k].r=r;if(l==r){tree[k].sum=val[idd[l]];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k); }void update(int k,int pos,int val) {if(tree[k].l==tree[k].r){tree[k].sum=val;return;}int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)update(k<<1,pos,val);elseupdate(k<<1|1,pos,val);pushup(k); }LL query(int k,int l,int r) {if(tree[k].r<l||tree[k].l>r)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].sum;return query(k<<1,l,r)+query(k<<1|1,l,r); }int son[N],num[N],deep[N],fa[N];//樹鏈剖分用void dfs1(int u,int f,int dep) {fa[u]=f;son[u]=-1;num[u]=1;deep[u]=dep;for(int i=0;i<node[u].size();i++){int v=node[u][i].v;int w=node[u][i].w;int id=node[u][i].id;if(v==f)continue;val[v]=w;//將邊權(quán)映射到點(diǎn)權(quán)上 rk[id]=v;dfs1(v,u,dep+1);num[u]+=num[v];if(son[u]==-1||num[son[u]]<num[v])son[u]=v;} }int top[N],tot;//樹鏈剖分用void dfs2(int u,int f,int root) {top[u]=root;id[u]=++tot;idd[tot]=u;if(son[u]!=-1)dfs2(son[u],u,root);for(int i=0;i<node[u].size();i++){int v=node[u][i].v;if(v==f||v==son[u])continue;dfs2(v,u,v);} }LL getnum(int x)//獲得點(diǎn)1-點(diǎn)x的權(quán)值和 {LL ans=0;while(top[x]!=1){ans+=query(1,id[top[x]],id[x]);x=fa[top[x]];}if(x==1)return ans;ans+=query(1,2,id[x]);//注意這里,雖然這給題目從1開始和從2開始沒什么區(qū)別return ans; }int LCA(int a,int b)//樹鏈剖分求lca {while(top[a]!=top[b]){if(deep[top[a]]<deep[top[b]])swap(a,b);a=fa[top[a]];}if(a==b)return a;return deep[a]<deep[b]?a:b; }int f[N];//并查集用 int find(int x)//并查集 {return x==f[x]?x:f[x]=find(f[x]); }void init()//初始化 {for(int i=1;i<=n;i++){f[i]=i;node[i].clear();}tot=0; }int main() { // freopen("input.txt","r",stdin); // ios::sync_with_stdio(false);int w;cin>>w;while(w--)//按照上文中一步步實(shí)現(xiàn)即可{scanf("%d%d",&n,&m);init();int mark;for(int i=1;i<=n;i++){int u,v;LL w;scanf("%d%d%lld",&u,&v,&w);edge[i].u=u;edge[i].v=v;edge[i].w=w;edge[i].id=i;int uu=find(u);int vv=find(v);if(uu!=vv){f[uu]=vv;node[u].push_back(Edge(u,v,w,i));node[v].push_back(Edge(v,u,w,i));}elsemark=i;}dfs1(1,0,0);dfs2(1,0,1);build(1,1,n);while(m--){int op,x,y;scanf("%d%d%d",&op,&x,&y);if(op==0){if(x==mark)edge[mark].w=y;elseupdate(1,id[rk[x]],y);}else{int lca=LCA(x,y);LL ans=getnum(x)+getnum(y)-2*getnum(lca);int u=edge[mark].u;int v=edge[mark].v;int w=edge[mark].w;LL ans1=getnum(x)+getnum(u)-2*getnum(LCA(x,u))+getnum(y)+getnum(v)-2*getnum(LCA(y,v));LL ans2=getnum(x)+getnum(v)-2*getnum(LCA(x,v))+getnum(y)+getnum(u)-2*getnum(LCA(y,u));printf("%lld\n",min(ans,min(ans1+w,ans2+w)));}}}return 0; }

    ?

    總結(jié)

    以上是生活随笔為你收集整理的HDU - 6393 Traffic Network in Numazu(线段树+LCA+树链剖分+并查集)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。