• [NOIp2016] 天天爱跑步


    洛谷·题目传送门

    洛谷上这道题居然有LCT的标签......真是吓人。

    实际上就是个LCA加上树上乱搞就行了。

    这道题难就难在思维,实际上编程实现的难度很小,尤其是找好方法之后。

    本人使用倍增LCA代码共2K,没怎么压行。

    第一眼一看,每个玩家的路线都是s到t的链。

    按照古老的套路,就是把 [ s 到 t ] 拆成 [ s 到根 ] + [ t 到根 ] - [ lca 到根 ] - [ lca的父亲到根 ] 四条链。

    简单起见,我们把 [ lca的父亲到根 ] 也变成 [ lca到根 ] 。

    这样每个点都对且只对他的祖先产生贡献。

    我们统计每个点子树的贡献和就好了。

    有个小bug:

    在统计的时候,我们统计的是子树的贡献和,并不包含该节点。

    所以如果在 s、t 和 lca 上打标记,只会影响他们的祖先节点的结果,对他们自身没有影响。

    所以我们特判 s、t 和 lca 的值。

    如果产生贡献(可正可负),就在统计之前先加上再说。

    这样就能统计每个点的答案啦~

    是不是还差了一点什么......

    是否产生贡献还与时间有关。

    我们设i的出发时间为st[i],点p的深度为d[p],w[i]的意义如题面所示。

    对于向上走的(s到根,两条lca到根之一),若在 p 处产生贡献,则有:start_time + d [ s ] - d [ p ] = w [ p ]

    移项,得:start_time + d [ s ]  = w [ p ] + d [ p ]

    这样等式右边只与 p 有关。

    我们可以建一个数组 h ,使 h [ start_time + d [ s ] ] ++(或--)(取决于贡献是1还是-1)

    实际上对于上行的所有链,start_time = 0

    我们查询一个点 p 的时候,h [ w [ p ] + d [ p ] ] 是多少,ans [ p ] 就+=多少。

    下行也是类似的做法。

    start_time - d [ lca ]  = w [ p ] - d [ p ]

    其中start_time = d [ s ] - d [ lca ]

    标记时h [ start_time - d [ lca ] ] ++(或--)

    查询时 ans [ p ] += h [ w [ p ] - d [ p ] ]

    一个点只对祖先产生贡献,所以我们不能直接改变 h 数组。

    我们在dfs的时候,每到一个点,把关于该点的标记全部加到 h 里,再dfs该点的子节点。

    dfs子节点前后答案的差值,就是子树内的贡献。

    关于一个点的标记可能有很多,使用类似链式前向星的方式记录下来。

    最后按点标号输出 ans [ i ] 。

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<algorithm>
      4 using namespace std;
      5 
      6 int n,m;
      7 int hd[300005],nx[600005],to[600005],cnt;
      8 int w[300005];
      9 
     10 void add(int af,int at)
     11 {
     12     to[++cnt]=at;
     13     nx[cnt]=hd[af];
     14     hd[af]=cnt;
     15 }
     16 
     17 int d[300005],f[300005][25];
     18 
     19 void pre(int p,int fa)
     20 {
     21     f[p][0]=fa;
     22     d[p]=d[fa]+1;
     23     for(int i=hd[p];i;i=nx[i])
     24     {
     25         if(to[i]!=fa)pre(to[i],p);
     26     }
     27 }
     28 
     29 int lca(int x,int y)
     30 {
     31     if(d[x]<d[y])swap(x,y);
     32     for(int i=20;i>=0;i--)
     33     {
     34         if(d[f[x][i]]>=d[y])x=f[x][i];
     35     }
     36     if(x==y)return x;
     37     for(int i=20;i>=0;i--)
     38     {
     39         if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
     40     }
     41     return f[x][0];
     42 }
     43 
     44 int ct[2];
     45 int st[300005][2],nxt[600005][2],k[600005][2],val[600005][2];
     46 int ans[300005];
     47 
     48 void mark(int p,int key,int v,int dir)
     49 {
     50     val[++ct[dir]][dir]=v;
     51     k[ct[dir]][dir]=key;
     52     nxt[ct[dir]][dir]=st[p][dir];
     53     st[p][dir]=ct[dir];
     54 }
     55 
     56 int h[900005][2];
     57 
     58 void dfs(int p,int fa)
     59 {
     60     for(int no=0;no<=1;no++)
     61         for(int i=st[p][no];i;i=nxt[i][no])
     62             h[k[i][no]+n][no]+=val[i][no];
     63     int tmp=(h[w[p]+d[p]+n][0]+h[w[p]-d[p]+n][1]);
     64     for(int i=hd[p];i;i=nx[i])if(to[i]!=fa)dfs(to[i],p);
     65     ans[p]+=(h[w[p]+d[p]+n][0]+h[w[p]-d[p]+n][1]-tmp);
     66 }
     67 
     68 int main()
     69 {
     70     scanf("%d%d",&n,&m);
     71     for(int i=1;i<n;i++)
     72     {
     73         int aa,bb;
     74         scanf("%d%d",&aa,&bb);
     75         add(aa,bb);
     76         add(bb,aa);
     77     }
     78     for(int i=1;i<=n;i++)scanf("%d",&w[i]);
     79     pre(1,0);
     80     for(int i=1;i<=20;i++)
     81     {
     82         for(int j=1;j<=n;j++)
     83         {
     84             f[j][i]=f[f[j][i-1]][i-1];
     85         }
     86     }
     87     cnt=0;
     88     for(int i=1;i<=m;i++)
     89     {
     90         int s,t;
     91         scanf("%d%d",&s,&t);
     92         int l=lca(s,t);
     93         mark(s,d[s],1,0);
     94         mark(l,d[s],-1,0);
     95         mark(t,d[s]-d[l]*2,1,1);
     96         mark(l,d[s]-d[l]*2,-1,1);
     97         if(w[l]==d[s]-d[l])ans[l]--;
     98         if(w[s]==0)ans[s]++;
     99         if(w[t]==d[s]+d[t]-d[l]*2)ans[t]++;
    100     }
    101     dfs(1,0);
    102     for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    103     return 0;
    104 }
    [NOIP2016]天天爱跑步
  • 相关阅读:
    2020软件工程第四次作业04
    2020软件工程作业02
    2020软件工程作业01
    2020软件工程个人作业06——软件工程实践总结作业
    2020软件工程作业05
    2020软件工程作业00—问题清单
    2020软件工程作业03
    2020软件工程作业02
    2020软件工程作业01
    小小小-冲刺集合
  • 原文地址:https://www.cnblogs.com/cervusy/p/9498631.html
Copyright © 2020-2023  润新知