zoukankan      html  css  js  c++  java
  • [NOIP2016] 天天爱跑步 桶 + DFS

    ~~~题面~~~

    题解:

    很久以前就想写了,一直没敢做,,,不过今天写完没怎么调就过了还是很开心的。

    首先我们观察到跑步的人数是很多的,要一条一条的遍历显然是无法承受的,因此我们要考虑更加优美的方法。

    首先我们假设观察者没有时间的限制,一天到晚都在观察。那么我们可以想到一个很显然的做法——差分。这样就可以很方便的求出一个点被经过了多少次。

    貌似这样再加想一下就可以直接用树链剖分+差分搞了。

    但是还有更好的算法,是O(n)的。

    而且还是比较好写的。

    首先我们观察可以被统计到的点要符合一个什么条件。

     

    设点j上的观察者在w[j]的时候观察,那么也就是说从s出发,要刚好经过w[j]到达点j,才可以被点j上的观察者统计到。

    由此我们可以列出两个式子:

    1,当s在j的子树内,而t在j的上方时。

      $w[j] + dep[j] = dep[s_i]$

    2,当t在j的子树内,而s在j的上方时

      $dis[i] - (dep[t_i] - dep[j]) = w[j]$ 其中dis[i]表示s ---> t的路程长度

      因为dis[i] - (dep[t_i] - dep[j])其实就是s ---> j的路径长,所以这个式子就显然成立了。

      如果我们将带i的放在左边,带j的放在右边,那么将会有

      $dis[i] - dep[t_i] = w[j] - dep[j]$

    那s和t都在j的子树内怎么办?(都在上方显然不会统计到)

      现在这时如果我们还是套用上面的式子,那么会有两种情况

      (1),j是(s,t)的LCA,那么此时两个式子一旦其中之一被满足,另外一个就一定会被满足(因为两个式子的实质是一样的),那么s和t将分别对j产生一次贡献(否则没有贡献)

      (2),j不是(s,t)的LCA,那么此时两个式子可能会被满足,s和t不一定会对j产生贡献。但是这种情况下,不论式子是否被满足,s和t都是不应该对j产生贡献的。
      那么我们怎么避免这种情况?

        首先我们注意到一旦这种情况出现,LCA[s,t]将会是s和t最后一次可能产生贡献的地方,因此我们要在LCA[s,t]处,消除s和t的影响,使得s和t无法对上面的点产生贡献。

    那么我们应该如何统计呢?

      注意到所有的式子都可以被表示为左边只有关于i的,右边只有关于j的情况,因此我们完全可以将式子的左边和右边单独计算。即分别用两个桶统计关于两个式子的满足情况。

    比如说我们定义int bu[600100];  然后每次遇到一个$s_i$的时候,我们就令$dep[s_i]++$。然后我们在每进入一个点时,都记录一个tmp = bu[w[j] + dep[j]],那么桶内对应位置元素的个数就代表满足

    $dep[s_i] = t$(t为桶中位置)的元素个数。因此我们访问bu[w[j] + dep[j]]就可以获取满足 $w[j] + dep[j] = dep[s_i]$ 的元素个数,而之所以要在进入之前先记录一下位置,则是为了准确获取子树内的贡献,保证不被之前的东西干扰。

      对第二个式子的处理方式也是类似的,只不过因为第二个式子中出现了减号,而且减号旁边大小关系不确定,因此我们需要的桶中位置可能为负,所以我们统一加上一个较大的数,将本来要占据负数的数组整体向后移位就可以了。

    具体实现看代码。

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 501000
      5 #define ac 910000//要开这么大。。。
      6 #define getchar() *o++
      7 char READ[12000100], *o = READ;
      8 int n, m;
      9 int w[AC], ans[AC], s[AC], t[AC], dis[AC], dep[AC], may[ac], id[ac];
     10 int Head[AC], Next[ac], date[ac], tot;
     11 struct edge{
     12     int Head[AC], Next[ac], date[ac], tot;
     13     inline void add(int f, int w)
     14     {
     15         date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
     16         date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
     17     }
     18 }E1,E2,E3;//询问的边(LCA),查询的边(ans) 
     19 
     20 inline int read()
     21 {
     22     int x = 0; char c = getchar();
     23     while(c > '9' || c < '0') c = getchar();
     24     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
     25     return x;
     26 }
     27 
     28 inline void add(int f, int w)
     29 {
     30     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
     31     date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
     32 }
     33 
     34 inline void add1(int f, int w, int S)
     35 {
     36     E3.date[++tot] = w, E3.Next[tot] = E3.Head[f], E3.Head[f] = tot, may[tot] = S;
     37 }
     38 
     39 inline void add2(int f, int w)//这里只能连单向边
     40 {
     41     E2.date[++tot] = w, E2.Next[tot] = E2.Head[f], E2.Head[f] = tot;
     42 }
     43 
     44 void pre()
     45 {
     46     int a, b;
     47     n = read(), m = read();
     48     for(R i = 1; i < n; i++)
     49     {
     50         a = read(), b = read();
     51         add(a, b);
     52     }
     53     for(R i = 1; i <= n; i++) w[i] = read();
     54     E1.tot = E2.tot = tot = 1;
     55     for(R i = 1; i <= m; i++) 
     56     {
     57         s[i] = read(), t[i] = read();
     58         E1.add(s[i], t[i]);
     59         add1(s[i], i, 0);//标记为开始节点
     60         add1(t[i], i, 1);//标记为结束节点
     61         id[E1.tot - 1] = id[E1.tot] = i;
     62     }
     63 }
     64 
     65 struct get_LCA{ 
     66     int father[AC], LCA[ac]; bool z[AC];
     67     
     68     inline int find(int x)
     69     {
     70          return (x == father[x]) ? x : father[x] = find(father[x]);
     71     }
     72     
     73     void dfs(int x)
     74     {
     75         int now;
     76         z[x] = true;
     77         for(R i = Head[x]; i; i = Next[i])
     78         {
     79             now = date[i];
     80             if(z[now]) continue; 
     81             dep[now] = dep[x] + 1;
     82             dfs(now);
     83             father[now] = x;//访问完就要改父亲了
     84         }
     85         for(R i = E1.Head[x]; i; i = E1.Next[i])
     86         {
     87             now = E1.date[i];
     88             if(z[now] && !LCA[i ^ 1]) 
     89             {
     90             //    printf("%d %d
    ", x, now);
     91                 LCA[i] = find(now);
     92                 dis[id[i]] = dep[x] - dep[LCA[i]] + dep[now] - dep[LCA[i]];
     93                 add2(LCA[i], id[i]);//将LCA和询问联系起来
     94             }
     95         }
     96     }
     97     
     98     void getLCA()
     99     {
    100         for(R i = 1; i <= n; i++) father[i] = i;
    101         dep[1] = 1;
    102         dfs(1);
    103     }
    104 }LCA;
    105 
    106 #define k 600000
    107 struct difference{
    108     int bu[ac], b[ac * 3]; 
    109     void dfs(int x, int fa)
    110     {
    111         int tmp = bu[dep[x] + w[x]], rnt = b[w[x] - dep[x] + k], now;
    112         for(R i = E3.Head[x]; i; i = E3.Next[i])//加上当前节点的贡献
    113         {
    114             now = E3.date[i];
    115             if(!may[i]) ++bu[dep[x]];//如果是开始节点
    116             else ++b[dis[now] - dep[x] + k];//等式两边同时+k
    117         }
    118         for(R i = Head[x]; i; i = Next[i])//遍历子树
    119         {
    120             now = date[i];
    121             if(now == fa) continue;
    122             dfs(now, x);
    123         }
    124         ans[x] = bu[dep[x] + w[x]] - tmp + b[w[x] - dep[x] + k] - rnt;
    125         for(R i = E2.Head[x]; i; i = E2.Next[i])//查看当前节点是哪些点对的LCA
    126         {
    127             now = E2.date[i];
    128             if(dep[s[now]] == dep[x] + w[x]) --ans[x];//如果造成了贡献,那么必定是双倍,因此要减去
    129             --bu[dep[s[now]]];//减去贡献
    130             --b[dis[now] - dep[t[now]] + k];//等式两边同时+k!
    131         }
    132     }
    133     
    134 }get;
    135 
    136 void work()
    137 {
    138     for(R i = 1; i <= n; i++) printf("%d ", ans[i]);
    139     printf("
    ");
    140 }
    141 
    142 int main()
    143 {
    144     freopen("in.in", "r", stdin);
    145     fread(READ, 1, 12000000, stdin);
    146     pre();
    147     LCA.getLCA();
    148     get.dfs(1, 0);
    149     work();
    150     fclose(stdin);
    151     return 0;
    152 }
  • 相关阅读:
    抽象工厂模式
    外观模式
    策略模式
    状态模式
    观察者模式
    装饰者模式
    模板方法模式
    适配器模式
    中介者模式
    组合模式
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9382100.html
Copyright © 2011-2022 走看看