AtCoder Beginner Contest 133 F - Colorful Tree

题意:给出一棵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\n",Q[i].ans);
    return 0;
} 

原文地址:https://www.cnblogs.com/clno1/p/11402711.html

时间: 2024-08-30 02:45:51

AtCoder Beginner Contest 133 F - Colorful Tree的相关文章

AtCoder Beginner Contest 152 - F - Tree and Constraints (容斥定理+树上路径的性质)

AtCoder Beginner Contest 152 - F - Tree and Constraints (容斥定理+树上路径的性质) We have a tree with NN vertices numbered 11 to NN. The ii-th edge in this tree connects Vertex aiai and Vertex bibi. Consider painting each of these edges white or black. There ar

AtCoder Beginner Contest 132 F - Small Products

数 sqrt 缩小范围 整除分块 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 1

AtCoder Beginner Contest 133 -D — Rain Flows into Dams

(https://atcoder.jp/contests/abc133/tasks/abc133_d) 思路:每座山为2Xi,每个坝为Ai.已知Ai,求出2Xi. 根据已知的X1,则可分别求出X2-n 代码: #include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; int a[maxn]; long long ans[maxn]; int main() { std::ios::sync_with_stdio(fal

AtCoder Beginner Contest 153 F - Silver Fox vs Monster

题目链接 https://atcoder.jp/contests/abc153/tasks/abc153_f 题意 : 在坐标轴上有一些怪兽,每个怪兽有对应的生命值hi,你可以对他们进行炮击,你的每次炮击可以队该点前后D范围内的怪兽造成A的伤害,问最少要炮击多少次. 我的最初的想法是先排序,扫到最左边的怪兽,先进行炮击,把他打死,然后记录炮击了多少次,然后把其后2d距离的怪兽都炮击一下发现超时 代码如下: inline ll read(){ ll s=0,w=1; char ch=getchar

AtCoder Beginner Contest 128 F - Frog Jump

题意 有一只青蛙,有\(0, 1, \cdots, N - 1\)个荷叶.每个荷叶上有权值\(s_i\). 选定\(A\), \(B\),初始分数为\(0\). 当前位置为\(x\): 对于\(y = x + A\): 如果\(y = N - 1\),游戏结束. 如果\(y \neq N - 1\),但是\(y\)这个荷叶存在,那么分数增加\(s_i\),并且这片荷叶消失. 如果\(y \neq N - 1\),但是\(y\)这个荷叶不存在,那么分数减去\(10^{100}\),游戏结束. 对于

AtCoder Beginner Contest 136

AtCoder Beginner Contest 136 Contest Duration : 2019-08-04(Sun) 20:00 ~ 2019-08-04(Sun) 21:40 Website: AtCoder BC-136 后面几题都挺考思考角度D. C - Build Stairs 题目描述: 有n座山从左到右排列,给定每一座山的高度\(Hi\),现在你可以对每座山进行如下操作至多一次:将这座山的高度降低1. 问是否有可能通过对一些山进行如上操作,使得最后从左至右,山的高度呈不下降

AtCoder Beginner Contest 154 题解

人生第一场 AtCoder,纪念一下 话说年后的 AtCoder 比赛怎么这么少啊(大雾 AtCoder Beginner Contest 154 题解 A - Remaining Balls We have A balls with the string S written on each of them and B balls with the string T written on each of them. From these balls, Takahashi chooses one

AtCoder Beginner Contest 155 简要题解

AtCoder Beginner Contest 155 A:签到失败,WA一次. int main() { int a, b, c; cin >> a >> b >> c; if(a == b && b == c) cout << "No"; else if(a == b || a == c || b == c) cout << "Yes"; else cout << &quo

【ATcoder】AtCoder Beginner Contest 161 题解

题目链接:AtCoder Beginner Contest 161 原版题解链接:传送门 A - ABC Swap 这题太水,直接模拟即可. 1 #include <iostream> 2 using namespace std; 3 int main() { 4 int a, b, c; 5 cin >> a >> b >> c; 6 swap(a, b); 7 swap(a, c); 8 cout << a << " &