zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 133 F

    题意:给出一棵n个点的树,每条边有颜色和边长两个属性,n个询问,每次询问把颜色x的边的边长变为y问u到v的路径长度是多少,询问之间独立。

    解法:这道题蛮有意思的。解法参考https://www.cnblogs.com/Tieechal/p/11185912.html这位大佬的,这里说下我的理解。

    对于每组询问(x,y,u,v)答案比较显然就是dist(u,v)+(sumlen[x]-sumcnt[x]*y),但是这道题在线不好做我们考虑离线做。但是答案的式子是设计到两个点的,怎么才能离线做呢?这里运用了一种比较巧妙地办法:利用LCA把询问拆成3个点,然后直接一遍dfs离线即可。具体来说就是把每个询问拆成u,v,lca三个点,这三个点对询问造成地贡献就是答案,每个点p的贡献就是:根到p点的路径+根到p点路径中颜色x的路径总和-根到p点颜色x的边数*y(当然三个点的系数是不一样的)。根据LCA求两点距离的经验不难看出其实这里的原理差不多,也是分别算跟到两点然后除去到lca的重复部分就是正确的。

    那么先dfs一遍求LCA,然后再离线dfs一遍处理询问就可以了。

    细节详见代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int n,m,t;
    typedef long long LL;
    struct query{
        int x,y,u,v;
        long long ans;
    }Q[N];
    vector<int> v[N],b[N];
    
    int cnt,head[N],nxt[N<<1],to[N<<1],col[N<<1],len[N<<1];
    void add_edge(int x,int y,int c,int d) {
        nxt[++cnt]=head[x]; to[cnt]=y; col[cnt]=c; len[cnt]=d; head[x]=cnt;
    }
    
    int dep[N],f[N][20];
    void dfs1(int x,int fa,int d) {
        dep[x]=d;
        for (int i=head[x];i;i=nxt[i]) {
            int y=to[i];
            if (y==fa) continue;
            f[y][0]=x;
            for (int j=1;j<=t;j++) f[y][j]=f[f[y][j-1]][j-1];
            dfs1(y,x,d+1);
        }
    }
    
    int LCA(int x,int y) {
        if (dep[x]>dep[y]) swap(x,y);
        for (int i=t;i>=0;i--)
            if (dep[f[y][i]]>=dep[x]) y=f[y][i];
        if (x==y) return x;
        for (int i=t;i>=0;i--)
            if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];        
    }
    
    int sumc[N],sumd[N];
    void dfs2(int x,int fa,int dis) {
        for (int i=0;i<v[x].size();i++) {
            int id=v[x][i];
            Q[id].ans+=(LL)dis*b[x][i];
            Q[id].ans-=(LL)sumd[Q[id].x]*b[x][i];
            Q[id].ans+=(LL)sumc[Q[id].x]*Q[id].y*b[x][i];
        }
        for (int i=head[x];i;i=nxt[i]) {
            int y=to[i];
            if (y==fa) continue;
            sumc[col[i]]++;
            sumd[col[i]]+=len[i];
            dfs2(y,x,dis+len[i]);
            sumc[col[i]]--;
            sumd[col[i]]-=len[i];
        }
    }
    
    int main()
    {
        cin>>n>>m;
        t=log2(n)+1;
        for (int i=1;i<n;i++) {
            int x,y,c,d;
            scanf("%d%d%d%d",&x,&y,&c,&d);
            add_edge(x,y,c,d);
            add_edge(y,x,c,d);
        }
        dfs1(1,0,1);
        for (int i=1;i<=m;i++) {
            scanf("%d%d%d%d",&Q[i].x,&Q[i].y,&Q[i].u,&Q[i].v);
            v[Q[i].u].push_back(i); b[Q[i].u].push_back(1);
            v[Q[i].v].push_back(i); b[Q[i].v].push_back(1);
            int lca=LCA(Q[i].u,Q[i].v);
            v[lca].push_back(i); b[lca].push_back(-2);
        }
        dfs2(1,0,0);
        for (int i=1;i<=m;i++) printf("%lld
    ",Q[i].ans);
        return 0;    
    } 
  • 相关阅读:
    多个漂亮的按钮样式和图片集合
    纯CSS3实现3D跳动小球
    visual studio 查找/替换对话框
    CSS实现弹出导航菜单
    javascript使浏览器关闭前弹出确认
    使用CSS3制作立体效果的导航菜单
    多个精美的导航样式web2.0源码
    jQuery实现侧边导航栏效果
    jQ函数after、append、appendTo的区别
    ASP.NET使用jQuery AJAX实现MD5加密实例
  • 原文地址:https://www.cnblogs.com/clno1/p/11402711.html
Copyright © 2011-2022 走看看