【NOIP模拟】图论题Graph
題面
n點2n-2條有向邊,數據先給一顆1為根的生成樹邊集,邊目錄按兩部分給出
1、 開始的 n-1 條邊描述了一顆以 1 號點為根的生成樹,即每個點都可以由 1 號點 到達。
2、 接下來的 N-1 條邊,一定是從 i 到 1(2<=i<=N)的有向邊,保證每個點都能到
然后給出除1外每個點到1的距離,q次詢問兩個操作:
1 x w將第x條邊的邊權修改為w
2 u v問u v最短距離
【輸入格式】 第一行是 2 個整數 N,Q,表示一共 N 個點 Q 次詢問 接下來是 N-1 行,每行 3 個整數 U,V,W,表示了前 N-1 條邊,u 到 v 的有向邊 接下來 N-1 行,每行 3 個整數 U,V,W,描述了每個點到 1 號點的邊,V==1 接下來是 Q 行,表示 Q 次修改與詢問
【輸出格式】 若干行,每行回答一次詢問
20%數據 沒有修改 30%數據 2<=N,Q<=1000 (其中有 10%數據沒有修改) 100%數據 2<=N,Q<=100 000, 1 <=邊權 <= 1000,000
分析
首先注意到了20%無修改,第一反應就想到了lca,因為lca的一大用處就是求樹上兩點距離
但是存在一個問題,這不是一棵普通的樹,根據題意,有兩種邊,前n-1條邊我們稱為樹邊,后n-1條邊我們稱為返祖邊
無修改的情況下,如果u是v的祖先,那就是純lca模板了,關鍵在于如果u不是v祖先呢?那u要通過返祖邊回到根節點,再從根節點走到v。但注意,u不僅可以從自身的返祖邊回去,還可以從子樹的返祖邊回去,所以在lca時需初始化出以u回到1節點的最小代價(即對于去到整個子樹每個節點的路徑以及返祖的邊綜合起來求最小)。再加上30%的1000的數據(遍歷子樹的每個點修改),暴力輕松得40分
—————————————————————以下為正解——————————————————————————
首先我們在暴力的時候就考慮到了進行預處理從任意節點回根的最小代價,但現在我們必須要修改了,所以還要考慮從根到此節點的路徑權值和。
定義dis[u]為從1->u->1的路徑值。把返祖邊記為rev[u],則1到u的樹上距離為dis[u]-rev[u]
查詢
則有兩種情況:1.u還是v的祖先,則答案是(dis[v]-rev[v])-(dis[u]-rev[u]) ?//兩樹邊相減
2.u不是v的祖先。需要在u的子樹中找出最小的dis[k]值,則答案是dis[k]-(dis[u]-rev[u])+(dis[v]-rev[v]) ?//u返祖路徑的值+從根到v的樹邊的值
根據上述思路,我們可以試著分兩類邊修改。
如果是任意節點u的樹邊,修改會影響到整棵子樹的dis[],但是如果是返祖邊只會影響u自己的dis[]。
思路大概告一段落
但在查詢和修改過程中發現了一系列問題:1、需要維護子樹的最小值。2、需要修改子樹的dis.
所以用線段樹維護dis,并通過dfs序編號(dfs序中一棵子樹的節點是連續的,便于操作)。用st[u]記錄以u為根的子樹的開始的dfs序,ed[u]為結束的節點dfs序
#include<bits/stdc++.h> using namespace std; #define N 200000 #define INF 0x7fffffff #define ll long long #define lc (p<<1) #define rc (p<<1|1) #define mid (t[p].l+t[p].r>>1) ll first[N],dep[N],dfn[N],st[N],ed[N],dis[N],rev[N],ux[N],vx[N],wx[N]; ll fa[N][20]; ll n,q,cnt,idx; struct email {ll u,v;ll w;ll nxt; }e[N*2]; struct NSD {ll l,r;ll minx,lazy; }t[N*4];inline void pushnow(ll p,ll val) {t[p].lazy+=val;t[p].minx+=val; }inline void pushup(ll p) {t[p].minx=min(t[lc].minx,t[rc].minx); }inline void pushdown(ll p) {if(t[p].lazy){pushnow(lc,t[p].lazy);pushnow(rc,t[p].lazy);t[p].lazy=0;} }void build(ll p,ll l,ll r) {t[p].l=l;t[p].r=r;if(l==r){t[p].minx=dis[l];t[p].lazy=0;return;}build(lc,l,mid);build(rc,mid+1,r);pushup(p); }void update(ll p,ll ql,ll qr,ll val) {if(ql<=t[p].l&&t[p].r<=qr){pushnow(p,val);return ;}pushdown(p);if(ql<=mid)update(lc,ql,qr,val);if(qr>mid)update(rc,ql,qr,val);pushup(p); }ll query(ll p,ll ql,ll qr) {ll ans=INF;if(ql<=t[p].l&&t[p].r<=qr)return t[p].minx;pushdown(p);if(ql<=mid)ans=min(ans,query(lc,ql,qr));if(qr>mid)ans=min(ans,query(rc,ql,qr));pushup(p);return ans; }inline void add(ll u,ll v,ll w) {e[++cnt].nxt=first[u];first[u]=cnt;e[cnt].u=u;e[cnt].v=v;e[cnt].w=w; }void dfs(ll u,ll f) {for(int i=1;(1<<i)<=dep[u];i++)fa[u][i]=fa[fa[u][i-1]][i-1];for(int i=first[u];i;i=e[i].nxt){int v=e[i].v;if(v==f)continue;dep[v]=dep[u]+1;fa[v][0]=u;dfs(v,u);} }ll lca(ll x,ll y) {if(dep[x]<dep[y])swap(x,y);ll t=dep[x]-dep[y];for(int i=0;(1<<i)<=t;i++) if(t&(1<<i))x=fa[x][i];if(x==y)return x;for(int i=19;i>=0;i--)if(fa[x][i]!=fa[y][i]){x=fa[x][i];y=fa[y][i];}return fa[x][0]; }void getdfn(ll u,ll f,ll w) {st[u]=++idx;dis[st[u]]=w+rev[u];for(int i=first[u];i;i=e[i].nxt){int v=e[i].v;if(v==f)continue;getdfn(v,u,w+e[i].w);}ed[u]=idx; }int main() {scanf("%lld%lld",&n,&q);for(int i=1;i<=(n-1)*2;i++){ll u,v,w;scanf("%lld%lld%lld",&u,&v,&w);ux[i]=u;vx[i]=v;wx[i]=w;if(i<n){add(u,v,w);add(v,u,w);}else rev[u]=w;}getdfn(1,0,0);dfs(1,0);build(1,1,n);for(ll i=1;i<=q;i++){ll x;scanf("%lld",&x);if(x==1){ll k,w;scanf("%lld%lld",&k,&w);if(k>=n){update(1,st[ux[k]],st[ux[k]],w-rev[ux[k]]);rev[ux[k]]=w;}else{update(1,st[vx[k]],ed[vx[k]],w-wx[k]);wx[k]=w;}}else{ll u,v;scanf("%lld%lld",&u,&v);if(lca(u,v)==u){ll du=query(1,st[u],st[u])-rev[u];ll dv=query(1,st[v],st[v])-rev[v];printf("%lld\n",dv-du);}else{ll du=query(1,st[u],ed[u])-query(1,st[u],st[u])+rev[u];ll dv=query(1,st[v],st[v])-rev[v];printf("%lld\n",du+dv);} }} return 0; }?
轉載于:https://www.cnblogs.com/NSD-email0820/p/9446553.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的【NOIP模拟】图论题Graph的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: node 根据图片img url 获取
- 下一篇: 1 微信公众号开发 服务器配置 有什么用