zoukankan      html  css  js  c++  java
  • NOIP2016天天爱跑步

    LuoguP1600

    这道题本来想着等初赛晚再切的....结果到了机房发现大佬们都切了qwq 没办法啊,就...大概看了一下,果然好难啊!QAQ

    Code:

      1 /*
      2 首先我们可以想到对于每个运动员都进行计算,但是复杂度太高了,所以我们转换思想
      3 对于每个观测点计算都有谁对它做出了贡献
      4 然后对于一条路径(s,t)我们把它拆开分为两部分s -> LCA(s,t) 和 LCA(s,t) -> t 
      5 然后就会出现两种情况:
      6 一:观测点p在 s -> LCA(s,t) 上,那么就是上行,那么满足dep[s]=w[p]+dep[p]的起点s就会对p产生贡献 
      7     我们用一个桶b1来维护上行产生的贡献值,对于每个观测点直接读取桶中b1[w[p]+dep[p]]的值即可
      8 二:观测点在 LCA(s,t) -> t 上,那么就是下行,那么满足dist[s,t]-w[p] = dep[t]-dep[p]移项后就是dist[s,t]-dep[t]=w[p]-dep[p]
      9     的终点t就会对p产生贡献
     10     我们用另一个桶b2来维护下行产生的贡献值,对于每个观测点直接读取桶中b2[w[p]-dep[p]]的值即可
     11 然后就是有几个注意的点:
     12 一:因为每个观察点只会被自己子树中的点影响,所以在开始时先读取桶中的值,在递归回溯前再减去算差值
     13 二:离开某个观测点后就要减去对应路径上桶中产生的贡献 
     14 三:当路径的起点或终点恰好为LCA时,上行下行路径会计算两遍,所以--
     15 */ 
     16 #include <bits/stdc++.h>
     17 using namespace std;
     18 const int N = 3e5 + 7;
     19 int n, m;
     20 int dep[N], w[N], fa[N][20];//节点深度  每个观察员观察时间   LCA数组 
     21 int b1[N * 2], b2[N * 2], js[N], dist[N], s[N], t[N], ans[N];
     22 //b1,b2分别是是上行下行产生贡献的桶  js是以某个点为起点的路径条数 
     23 //dist[i]是第i条路径的长度  s[],t[]分别是每条路径的起点终点 ans是答案 
     24 int tot, tot1, tot2, head[N], head1[N], head2[N];
     25 struct edge{
     26     int next, to;
     27 }e[N * 3], e1[N * 3], e2[N * 3];
     28 void add(int u, int v) {//就加基础的边 
     29     e[++tot] = {head[u], v};
     30     head[u] = tot;
     31 }
     32 void add1(int u, int v) {//把每条路径的终点和该条路径编号连边 用于计算下行贡献 
     33     e1[++tot1] = {head1[u], v};
     34     head1[u] = tot1;
     35 }
     36 void add2(int u, int v) {//把每条路径起点终点LCA与该条路径编号连边 用于清除 
     37     e2[++tot2] = {head2[u], v};
     38     head2[u] = tot2;
     39 }
     40 void dfs1(int u) {//第一遍dfs就求出父亲和深度 
     41     for (int i = 1; (1 << i) <= dep[u]; i++) {
     42         fa[u][i] = fa[fa[u][i - 1]][i - 1];
     43     }
     44     for (int i = head[u]; i; i = e[i].next) {
     45         int to = e[i].to;
     46         if (to == fa[u][0]) {
     47             continue;
     48         }
     49         fa[to][0] = u;
     50         dep[to] = dep[u] + 1;
     51         dfs1(to);
     52     }
     53 }
     54 int LCA(int x, int y) {//倍增求LCA 
     55     if (x == y) {
     56         return x;
     57     }
     58     if (dep[x] < dep[y]) {
     59         swap(x, y);
     60     }
     61     int dd = dep[x] - dep[y];
     62     for (int i = 0; i < 20; i++) {
     63         if (dd >> i & 1) {
     64             x = fa[x][i];
     65         }
     66     }
     67     if (x == y) {
     68         return x;
     69     }
     70     for (int i = 19; i >= 0; i--) {
     71         if (fa[x][i] != fa[y][i]) {
     72             x = fa[x][i];
     73             y = fa[y][i];
     74         }
     75     }
     76     return fa[x][0];
     77 }
     78 void dfs2(int u) { 
     79     int t1 = b1[dep[u] + w[u]];//因为每个观察点只会被自己子树中的点影响 
     80     int t2 = b2[w[u] - dep[u] + N];//所以在开始时先读取桶中的值,在递归回溯前再减去算差值 
     81     for (int i = head[u]; i; i = e[i].next) {
     82         int to = e[i].to;
     83         if (to == fa[u][0]) {
     84             continue;
     85         }
     86         dfs2(to);
     87     }
     88     b1[dep[u]] += js[u];//计算上行贡献 加上以该点为起点的路径条数 
     89     for (int i = head1[u]; i; i = e1[i].next) {//计算下行贡献 
     90         int to = e1[i].to;//取出以这个点为终点的路径遍号 
     91         b2[dist[to] - dep[t[to]] + N]++;///根据公式计算贡献 dist[s,t]-dep[t] = w[u] - dep[u] 
     92     }
     93     ans[u] += b1[w[u] + dep[u]] - t1 + b2[w[u] - dep[u] + N] - t2;//计算差值 
     94     for (int i = head2[u]; i; i = e2[i].next) {
     95         int to = e2[i].to;//取出以改点为LCA的路径编号 
     96         b1[dep[s[to]]]--; //因为离开当前观察点之后当前贡献就无效了,所以减去 
     97         b2[dist[to] - dep[t[to]] + N]--;
     98     }
     99 }
    100 int main () {
    101     scanf("%d%d", &n, &m);
    102     for (int i = 1; i < n; i++) {
    103         int u, v;
    104         scanf("%d%d", &u, &v);
    105         add(u, v);
    106         add(v, u);
    107     }
    108     dep[1] = 1;
    109     fa[1][0] = 1;
    110     dfs1(1);
    111     for (int i = 1; i <= n; i++) {
    112         scanf("%d", &w[i]);
    113     }
    114     for (int i = 1; i <= m; i++) {
    115         scanf("%d%d", &s[i], &t[i]);
    116         int lca = LCA(s[i], t[i]);
    117         dist[i] = dep[s[i]] + dep[t[i]] - 2 * dep[lca];//树上差分 
    118         js[s[i]]++;
    119         add1(t[i], i);
    120         add2(lca, i);
    121         if (dep[lca] + w[lca] == dep[s[i]]) { 
    122             ans[lca]--;//这里是因为如果路径的起点或终点恰好为LCA时,上行下行路径会计算两遍,所以--
    123         }
    124     }
    125     dfs2(1);
    126     for (int i = 1; i <= n; i++) {
    127         printf("%d%c", ans[i], i == n ? '
    ' : ' ');
    128     }
    129     return 0;
    130 }
    View Code
  • 相关阅读:
    【林】Ubuntu下安装和设置 OpenSSH Server
    吐吐槽
    【不定时推荐】这些年读过的书第一本--《一个人的朝圣》
    source insight 和keil 编辑对齐
    WeifenLuo DockContent停靠窗口的大小设置
    指针无法保存值
    php 练习基础
    php 写日志
    数据库设计——字段类型设计
    .net用TreeView实现CheckBoxes全选 反选 子节选中 传值
  • 原文地址:https://www.cnblogs.com/Sundial/p/11830503.html
Copyright © 2011-2022 走看看