HDU-6393 Traffic Network in Numazu 树状数组维护树上差分环基数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lifelikes/article/details/81672937

PS- 真的想吐槽电子科大的出题人。 数据水也算了,还有两题的标程是错的,还各种卡常,各种原题。

Traffic Network in Numazu
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 456 Accepted Submission(s): 179

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

Recommend
chendu

题意 :
给出一个只有一个环的树,有边权无点权
支持两种操作 :
1。 修改某条边的权值。
2。求一条链上的权值之和。

解题思路:
1。这是一颗带有一个环的树,环基数的常用解题思路是,去除一条边,转化成了一个树上修改,查询问题、然后计算去除那一条边对答案的贡献。
树上带修改查询问题 常用的有树链剖分。
不过这个题转化成树的问题后,是一个裸的树状数组维护树上差分的模板题。
干货点这里——dfs序常见题型
emmmmmm
理解了树上差分这个题就是一个很傻逼的题目了。
但是,我不会树上差分。。。。 然后网上又没有详细解释的博客。 走了一些弯路。

树状数组维护树上差分可以在logn的代价内进行区间更新,单点查询的操作。(我一直以为是区间查询。。 想了好久) 非常优秀。
主要利用的思想就是前缀和思想。
由于dfs序有一个特殊性质,就是一颗子树的所有子节点的dfs序号是连续的,我们可以根据这个,将树拉成链。 对于每各节点,考虑其子树的所有节点贡献。 然后利用前缀和思想维护。

扫描二维码关注公众号,回复: 3448039 查看本文章

然后这个问题就很好解决了。
考虑去除的边对答案的贡献。 就是求出选择走去除的边的长度,然后取最小就可以了。
还有一些细节问题:
由于是边权,求lca后图就变成了一颗有根树,所以需要在dfs的时候记录一下每条边最后选择的方向。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX = 1e5+777;
class Edge{
public:
    int u,v,next;
    long long w;
};
int n;
int tot;
int head[MAX];
Edge edge[MAX<<1];
long long X,Y,W,ID;
bool vis[MAX];
void init(int n){
    for(int i=0;i<=n;i++){
        head[i]=-1;
        vis[i]=0;
    }
    tot=0;
}
void add(int u,int v,int w){
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
//《----------------LCA && dfs序-------------------》
int deep[MAX];
int fa[MAX][23];
int L[MAX],R[MAX];
int E[MAX];
int edgeid[MAX];
int edgew[MAX];
int dfsnum=0;
int LCADFS(int u,int _deep,int _fa){
    vis[u]=1;
    L[u]=(++dfsnum);
    edgeid[L[u]] = u;
    deep[u]=_deep;
    fa[u][0]=_fa;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==_fa){
            continue;
        }
        if(vis[v]){
            X=u,Y=v,W=edge[i].w;
            ID = (i>>1) +1;
            continue;
        }
        //fa[v][0]=u;
        edgew[v]=edge[i].w;
        E[(i>>1)+1]=v;
        LCADFS(v,_deep+1,u);
    }
    R[u]=dfsnum;
}
void presove(){
    dfsnum=0;
    LCADFS(1,0,1);
    for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            fa[j][i]=fa[fa[j][i-1]][i-1];
        }
    }
}
int LCA(int u,int v){
    while(deep[u]!=deep[v]){
        if(deep[u]<deep[v]){
            swap(u,v);
        }
        int d= deep[u] -deep[v];
        for(int i=0;i<=20;i++){
            if(d>>i &1) u=fa[u][i];
        }
    }
    if(u==v) return u;
    for(int i=20;i>=0;i--){
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}
//<---------树状数组--------------->
struct Tree {
    int n;
    vector <LL> T;
    void init (int _n) {
        if (T.empty() ) T.resize (MAX<<1);
        else for (int i = 0; i <= _n; i++)
                T[i] = 0;
        n = _n;
    }
    void add (int x, int v) {
        if (x <= 0) return;
        for (int i = x; i <= n; i += i & -i)
            T[i] += v;
    }
    LL sum (int x) {
        if (x > n) x = n;
        LL ret = 0;
        for (int i = x; i > 0; i -= i & -i)
            ret += T[i];
        return ret;
    }
} t1;
long long dist(int x,int y){
    int thelca = LCA(x,y);
    //cout<<thelca<<endl;
    return t1.sum(L[x])+t1.sum(L[y])-2ll*(t1.sum(L[thelca]));
}
int main(){
    int T;
    //freopen("k.in","r",stdin);
    scanf("%d",&T);
    while(T--){
        int q;
        scanf("%d %d",&n,&q);
        init(n);
        t1.init(n);
        int u,v,w;
        for(int i=1;i<=n;i++){
            scanf("%d %d %d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        presove();
        for(int i=1;i<=n;i++){
            t1.add(L[edgeid[i]],edgew[edgeid[i]]);
            t1.add(R[edgeid[i]]+1,-edgew[edgeid[i]]);
        }
        int op,x,y;
        while(q--){
            //cout<<X<<","<<Y<<","<<W<<endl;
            scanf("%d %d %d",&op,&x,&y);
            if(op){
                long long ans = dist(x,y);
                ans = min(ans,dist(x,X)+dist(y,Y)+W);
                ans = min(ans,dist(y,X)+dist(x,Y)+W);
                printf("%lld\n",ans);
            }else{
                if(ID == x){
                    W=y;
                }else{
                    t1.add(L[E[x]],y-edgew[E[x]]);
                    t1.add(R[E[x]]+1,-y+edgew[E[x]]);
                    edgew[E[x]] = y;
                }
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/81672937