hdu 6393(环套树+lca)

Traffic Network in Numazu

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 510    Accepted Submission(s): 196


 

Problem Description

Chika is elected mayor of Numazu. She needs to manage the traffic in this city. To manage the traffic is too hard for her. So she needs your help. 
You are given the map of the city —— an undirected connected weighted graph with N nodes and N edges, and you have to finish Q missions. Each mission consists of 3 integers OP, X and Y. 
When OP=0, you need to modify the weight of the Xth edge to Y. 
When OP=1, you need to calculate the length of the shortest path from node X to node Y.

 

 

Input

The first line contains a single integer T, the number of test cases. 
Each test case starts with a line containing two integers N and Q, the number of nodes (and edges) and the number of queries. (3≤N≤105)(1≤Q≤105) 
Each of the following N lines contain the description of the edges. The ith line represents the ith edge, which contains 3 space-separated integers ui, vi, and wi. This means that there is an undirected edge between nodes ui and vi, with a weight of wi. (1≤ui,vi≤N)(1≤wi≤105) 
Then Q lines follow, the ith line contains 3 integers OP, X and Y. The meaning has been described above.(0≤OP≤1)(1≤X≤105)(1≤Y≤105) 
It is guaranteed that the graph contains no self loops or multiple edges.

 

 

Output

For each test case, and for each mission whose OP=1, print one line containing one integer, the length of the shortest path between X and Y.

 

 

Sample Input

 

2 5 5 1 2 3 2 3 5 2 4 5 2 5 1 4 3 3 0 1 5 1 3 2 1 5 4 0 5 4 1 5 1 5 3 1 2 3 1 3 2 3 4 4 4 5 5 2 5 5 0 1 3 0 4 1 1 1 4

 

 

Sample Output

 

5 6 6 6

 

 

Source

2018 Multi-University Training Contest 7

题意: 给n个点和n条边,两种操作,操作0 改变某条边的权值,操作1 询问某两个点的最短路?

思路: n个点,n条边,那么一定是一棵树带1个环,考虑仅仅是树的情况,我可以求个dfs序,然后找到某个点管辖的区间,然后每次更新这条边,就可以用线段树维护这条边所影响的点,那么就是更新这条边下边的点管辖的区间,然后就是求个LCA 得到结果,但是现在问题在于环,其实我可以选择任意一条环上的边,然后考虑去掉这条边,那么就转化成上边的问题了,最后我考虑加上这条边的影响,那么如果u到v经过环的话,那么肯定会多出来两条边,就是u-> uu -> vv->v 和u->vv->uu->u 这两条边,加上这两条边的影响,那么问题就可以解决了。  代码写的跟shi一样。。。

另外测试了一下,数据就三个样例,而且三个样例最后一条边都在环上,所以直接用最后一条边当作环上的边也能过。。。

代码: 

#include<bits/stdc++.h>
#define lson (i<<1)
#define rson (i<<1|1)

using namespace std;
typedef long long ll;
const int N =1e5+5;

struct node
{
    int u,v,next;
    int id,f;
    ll w;
}edge[N*2];

struct node1
{
    int l,r;
    ll w;
    ll lz;
}tr[N<<2];

int tot,head[N];
int n,q;
int anc[N<<1][20];

int dfn;
int dfns[N*2];
int dep[N*2];
int pos[N];
int inde[N];
int L[N];
int R[N];
int clo;

int to[N];
int vis[N];
ll ww[N];

int uu,vv;
ll cost;
int huan;
int idd;

void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(inde,-1,sizeof(inde));
    memset(pos,-1,sizeof(pos));
    clo=0; huan=0; idd=0; dfn=0;  /// dfn竟然没清零 MMP
}

void add(int u,int v,ll w,int id)
{
    edge[++tot].u=u; edge[tot].v=v; edge[tot].id=id; edge[tot].w=w; edge[tot].f=0;
    edge[tot].next=head[u]; head[u]=tot;
}

void dfs1(int u,int fa)
{
    if(vis[u]){
        uu=fa; vv=u; huan=1; return ;
    }
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==fa) continue;
        dfs1(v,u);
    }
}

void dfs2(int u,int deep) /// dfs序
{
    //cout<<" u "<<u<<" deep "<<deep<<endl;
    dfns[dfn]=u; dep[dfn]=deep; pos[u]=dfn++;
    L[u]=++clo;
    inde[u]=L[u]; /// 记录u在线段树中的位置
    for(int i=head[u];i!=-1;i=edge[i].next){
        if(edge[i].f) continue;  /// 如果是标记的边跳过
        int v=edge[i].v;
        if(pos[v]==-1){
            to[edge[i].id]=v; /// 表示这条边指向哪个点?
            dfs2(v,deep+1);
            dfns[dfn]=u; dep[dfn++]=deep;
        }
    }
    R[u]=clo;
}

void init_RMQ(int n)  /// dfn
{
    for(int i=1;i<=n;++i) anc[i][0]=i;
    for(int j=1;(1<<j)<=n;++j)
        for(int i=1;i+(1<<j)-1<=n;++i){
            if(dep[anc[i][j-1]]<dep[anc[i+(1<<(j-1))][j-1]]) anc[i][j]=anc[i][j-1];
            else anc[i][j]=anc[i+(1<<(j-1))][j-1];
        }
}

inline int RMQ(int L,int R)
{
    int k=0;
    while(1<<(k+1)<=R-L+1) ++k;
    if(dep[anc[L][k]]<dep[anc[R-(1<<k)+1][k]]) return anc[L][k];
    return anc[R-(1<<k)+1][k];
}

inline int LCA(int u,int v)
{
    if(pos[u]>pos[v]) return dfns[RMQ(pos[v],pos[u])];
    return dfns[RMQ(pos[u],pos[v])];
}

void push_up(int i)
{
    tr[i].w=tr[lson].w+tr[rson].w;
}

void push_down(int i)
{
    if(tr[i].lz){ /// 查询只有点查询,所以不必更新区间点的sum
        ll &lz=tr[i].lz;
        tr[lson].lz+=lz; tr[rson].lz+=lz;
        tr[lson].w+=lz;  tr[rson].w+=lz;
        lz=0;
    }
}

void build(int i,int l,int r)
{
    tr[i].l=l; tr[i].r=r; tr[i].w=0; tr[i].lz=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
}

void update(int i,int l,int r,ll w)
{
    if(tr[i].l==l&&tr[i].r==r){
        tr[i].lz+=w; tr[i].w+=w;
        return ;
    }
    push_down(i);
    int mid=(tr[i].l+tr[i].r)>>1;
    if(r<=mid) update(lson,l,r,w);
    else if(l>mid ) update(rson,l,r,w);
    else{
        update(lson,l,mid,w);
        update(rson,mid+1,r,w);
    }
    push_up(i);
}

ll query(int i,int aim)
{
    if(tr[i].l==tr[i].r&&tr[i].l==aim){
        return tr[i].w;
    }
    push_down(i);
    int mid=(tr[i].l+tr[i].r)>>1;
    if(aim<=mid) return query(lson,aim);
    else return query(rson,aim);
}

ll getans(int u,int v)
{
    int lca=LCA(u,v);
    ll sum1,sum2,sum3;
    sum1=query(1,L[u]); sum2=query(1,L[v]);
    sum3=query(1,L[lca]);
    return sum1+sum2-sum3*2;
}

int main()
{
    int T;
    int u,v,op;
    ll w;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&q);
        init();
        for(int i=1;i<=n;i++){
            scanf("%d %d %lld",&u,&v,&w);
            add(u,v,w,i);
            add(v,u,w,i);
            ww[i]=w;
        }

        dfs1(1,-1);/// 第一遍dfs 先找到环中的任意一条边
        for(int i=1;i<=tot;i++){  /// 给边打上标记
            if((edge[i].u==uu&&edge[i].v==vv)||(edge[i].u==vv&&edge[i].v==uu)){
                edge[i].f=1;
                idd=edge[i].id;
                cost=edge[i].w;
            }
        }
        dfs2(1,0);
        /*cout<<"dfn "<<dfn<<endl;
        cout<<uu<<" *** "<<vv<<endl;
        for(int i=1;i<=n;i++){
            cout<<"l "<<L[i]<<" r "<<R[i]<<endl;
        }
        */
        init_RMQ(dfn);


        build(1,1,n);  /// 以dfs的遍历出的 L,R 建树  那么接下来就是一个区间更新,单点查询的问题了
        for(int i=1;i<=n;i++){
            if(i==idd) continue;
            u=to[i];
            update(1,L[u],R[u],ww[i]);
        }
        /*
        for(int i=1;i<=n;i++){
            ll tmp=query(1,L[i]);
            cout<<" dis "<<tmp<<endl;
        }
        */

        while(q--)
        {
            scanf("%d",&op);
            if(op==0){
                scanf("%d %lld",&u,&w);
                if(u==idd){ /// 如果是标记的环中的边,那么就没必要更新线段树
                    ww[u]=w;
                }
                else{
                    int tmp=to[u];
                    //cout<<"tmp "<<tmp<<" L "<<L[tmp]<<" R "<<R[tmp]<<" w "<<ww[u]<<endl;
                    update(1,L[tmp],R[tmp],-ww[u]);
                    ww[u]=w;
                    update(1,L[tmp],R[tmp],ww[u]);
                }
            }
            else{
                scanf("%d %d",&u,&v);
                ll ans=getans(u,v);
                ans=min(ans,getans(uu,u)+getans(vv,v)+cost); /// 经过标记的路的两个不同的方向。
                ans=min(ans,getans(uu,v)+getans(vv,u)+cost);
                printf("%lld\n",ans);
            }
        }

    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值