#LOJ2359 UOJ261 NOIP2016 天天爱跑步

题目链接
说实话一开始这个题目题解我是完全没看懂的

哪怕我把天天不爱跑步做完了也一样…..

还是对题目理解的不太清楚啊

首先把对于任意一条路径 s>t s − > t ,我们可以把它拆成 s>lca(s,t) s − > l c a ( s , t )
lca(s,t)>t l c a ( s , t ) − > t 然后处理就会比较方便了

之前做谈笑风生这个题的时候也用过一个这样的思想。

如果某个点子树内某种深度的点对答案有贡献。

那么可以把每个深度的信息存下来,相当于开个桶。

入栈的时候减去全局这个深度的信息,出栈的时候加回来就好了

联系一下 dfs d f s 相关的东西应该不是很难理解

这道题我们也可以这样子 对于向上的路径

如果 x x i有贡献 那么一定满足 dep[x]=dep[i]+w[i] d e p [ x ] = d e p [ i ] + w [ i ]

就每次遍历到起点就 cnt[dep[x]] c n t [ d e p [ x ] ] 1 1 就好了

lca出栈的时候再减掉就好了

算答案的时候算的就是 cnt[dep[i]+w[i]] c n t [ d e p [ i ] + w [ i ] ] 的值

向下的路径可以同样考虑

如果 x x i有贡献 那么一定满足 dep[i]dep[lca]=w[i](dep[x]dep[lca]) d e p [ i ] − d e p [ l c a ] = w [ i ] − ( d e p [ x ] − d e p [ l c a ] )

意义就是 lca l c a i i 所花的时间与i出现观察员的时间减去路径上已经花费的时间

移项可得 dep[lca]×2dep[x]=dep[i]w[i] d e p [ l c a ] × 2 − d e p [ x ] = d e p [ i ] − w [ i ]

由于这次我们统计的是 lca l c a y y 之间的路径,所以也是在lca上减回来

y y 加就好了 算答案算的时候是cnt[dep[i]w[i]]的值

这个下标可能是负的 用 std::map s t d :: m a p 类似的东西就可以解决了

复杂度 O(nlogn) O ( n l o g n )

Codes
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>

#define pb push_back

using namespace std;

__gnu_pbds::gp_hash_table<int, int> cnt1, cnt2;
const int N = 6e5 + 10;
vector<int> G[N], ins1[N], del1[N], ins2[N], del2[N];
int dep[N], fa[N][21];
int w[N], ans[N];
int n, m;

void dfs(int x) {
    for(auto v : G[x])
        if(!dep[v]) {
            dep[v] = dep[x] + 1;
            fa[v][0] = x;
            dfs(v);
        }
}

int lca(int x, int y) {
    if(dep[x] != dep[y]) {
        if(dep[x] < dep[y]) swap(x, y);
        for(int j = 20; ~ j; -- j)
            if(dep[fa[x][j]] > dep[y])
                x = fa[x][j];
        x = fa[x][0];
    }
    if(x == y) return x;
    for(int j = 20; ~ j; -- j)
        if(fa[x][j] != fa[y][j]) 
            x = fa[x][j], y = fa[y][j];
    return fa[x][0];
}

void dfs1(int u, int dad) {
    ans[u] -= cnt1[dep[u] + w[u]];
    for(auto i : ins1[u]) {
        ++ cnt1[i];
    //  cout << "ADD " << u << ' ' << i << endl;
    }
    for(auto v : G[u])
        if(v != dad)
            dfs1(v, u);
    //if(u == 1) cout << ans[1] << endl;
    ans[u] += cnt1[dep[u] + w[u]];
    //if(u == 1) cout << dep[u] + w[u] << ' ' << ans[1] << endl;
    for(auto i : del1[u]) {
        -- cnt1[i];
        //cout << "DEL " << u << ' ' << i << endl;
    }
    //if(u == 2) cout << ans[2] << endl;
}

void dfs2(int u, int dad) {
    ans[u] -= cnt2[dep[u] - w[u]];
    for(auto i : ins2[u]) {
        ++ cnt2[i];
//      cout << "ADD " << u << ' ' << i << endl;
    }
    for(auto v : G[u])
        if(v != dad) 
            dfs2(v, u);
    ans[u] += cnt2[dep[u] - w[u]];
    for(auto i : del2[u]) {
        -- cnt2[i];
    //  cout << "DEL " << u << ' ' << i << endl;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("2359.in", "r", stdin);
    freopen("2359.out", "w", stdout);
#endif
    int x, y, z;
    scanf("%d%d", &n, &m);
    for(int i = 1; i < n; ++ i) {
        scanf("%d%d", &x, &y);
        G[x].pb(y), G[y].pb(x);
    }
    for(int i = 1; i <= n; ++ i)
        scanf("%d", &w[i]);
    dfs(dep[1] = 1);
    for(int j = 1; j <= 20; ++ j)
        for(int i = 1; i <= n; ++ i)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    for(int i = 1; i <= m; ++ i) {
        scanf("%d%d", &x, &y);
        int f = lca(x, y); z = dep[x] - dep[f];
        ins1[x].pb(dep[x]); del1[f].pb(dep[x]);
        ins2[y].pb(dep[f] - z); del2[f].pb(dep[f] - z);
        //cout << f << ' ' << y << endl;
        if(dep[f] + w[f] == dep[x]) -- ans[f];
    }
    dfs1(1, 0), dfs2(1, 0);
    for(int i = 1; i <= n; ++ i)
        printf("%d ", ans[i]);
    return 0;
}

感觉自己主要毛病就是没太看懂题就开题了 以后还是不要太毛躁 老老实实看完

再根据自己会做的分来分配时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值