• 【NOIP2016】【LCA】【树上差分】【史诗级难度】天天爱跑步


    学弟不是说要出丧题吗》》所以我就研究了1天lca又研究了1天tj然后研究了一天天天爱跑步,终于写了出来。(最后的平均用时为240ms。。。比学弟快了1倍。。。)

    题意:给你颗树,然后有m个东西在树上移动,每s移动一条边。路径为S[i]->T[i],现在求在w[i]时第i各节点上有多少个东西。

    解题思路:首先,我们考虑使用lca将走过的路径拆成2半,一段为S->f(lca),一段为f(lca)->T,显然对于一个点在T时所经过他的东西数,我们是可以通过树上差分求出的,具体方法:首先讲一下从S->f的做法,考虑按深度差分,显然第i个人第0s的位置为deep[S],于是记此时差分数组中deep[S]--,这样若一个人在w[i]时经过点i,显然我们可以认为差分数组中的[deep[i]-w[i]]就可以代表了这个点的答案,但显然这是过于理想化的考虑,显然对于一个节点,其子树外的情况也会影响到这个点的数值,因而我们需要在遍历以这个节点为根的子树前先减去差分数组的值,这样就可以使得答案一定是子树内的情况了。从f->T也同样如此可以完成,这里不再赘述,具体实现请参考AC代码,注意一些细节的处理:).

    AC代码:

     1 #include<stdio.h>
     2 #define MN 300005
     3 #define nt edge[i].to
     4 struct zxy{int to,next;}edge[MN*8];//链表(把多个链表都塞进去了)
     5 int cnt,h[MN],q[MN],adh1[MN],deh1[MN],adh2[MN],deh2[MN];//各链表表头
     6 int qans[MN],tim[MN],deep[MN],n,m,chafen[MN*3],fa[MN],ans[MN],x[MN],y[MN];
     7 bool vis[MN];
     8 inline void ins(int *h,int x,int y){edge[++cnt].next=h[x],edge[cnt].to=y,h[x]=cnt;}//构造链表
     9 inline int getfa(int x){return fa[x]?fa[x]=getfa(fa[x]):x;}//并查集
    10 inline int in(){
    11     int x=0,f=1;char ch=getchar();
    12     while(ch<'0'||ch>'9') f=ch=='-'?-1:1,ch=getchar();
    13     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    14     return x*f;
    15 }
    16 inline void tjlca(int x,int d){
    17     deep[x]=d;vis[x]=1;
    18     for (register int i=h[x]; i; i=edge[i].next) 
    19         if (!vis[nt])tjlca(nt,d+1),fa[nt]=x;
    20     for (register int i=q[x]; i; i=edge[i].next)
    21         if (qans[nt]) qans[nt]=getfa(qans[nt]);
    22         else qans[nt]=x;
    23 }//tarjan算法求LCA
    24 inline void dfs1(int u){
    25     vis[u]=0;ans[u]-=chafen[deep[u]-tim[u]];
    26     for (register int i=h[u]; i; i=edge[i].next) if (vis[nt]) dfs1(nt);
    27     for (register int i=adh1[u]; i; i=edge[i].next) ++chafen[nt];
    28     ans[u]+=chafen[deep[u]-tim[u]];
    29     for (register int i=deh1[u]; i; i=edge[i].next) --chafen[nt];
    30 }//dfs处理S->f的
    31 inline void dfs2(int u){
    32     vis[u]=1;ans[u]-=chafen[deep[u]+tim[u]];
    33     for (register int i=h[u]; i; i=edge[i].next) if (!vis[nt]) dfs2(nt);
    34     for (register int i=adh2[u]; i; i=edge[i].next) ++chafen[nt];
    35     ans[u]+=chafen[deep[u]+tim[u]];
    36     for (register int i=deh2[u]; i; i=edge[i].next) --chafen[nt];
    37 }//dfs处理f->T的
    38 void read(){
    39     n=in(),m=in();int u,v;
    40     for (int i=1; i<n; ++i) u=in(),v=in(),ins(h,u,v),ins(h,v,u);
    41     for (register int i=1; i<=n; ++i) tim[i]=in()-MN;//为了防止减法出现负数,所以我们要这么做。
    42     for (register int i=1; i<=m; ++i) x[i]=in(),y[i]=in(),ins(q,x[i],i),ins(q,y[i],i);
    43 }//输入
    44 void init(){
    45     tjlca(1,0);
    46     for (register int i=1; i<=m; ++i){
    47         int f=qans[i],u=x[i],v=y[i];
    48         if (f==v) ins(adh2,u,deep[u]+MN),ins(deh2,v,deep[u]+MN);
    49         else{
    50             if (f==u) ins(adh1,v,deep[u]+MN),ins(deh1,u,deep[u]+MN);
    51             else{
    52                 ins(adh2,u,deep[u]+MN);
    53                 ins(deh2,f,deep[u]+MN);
    54                 ins(adh1,v,(deep[f]<<1)-deep[u]+MN);
    55                 ins(deh1,f,(deep[f]<<1)-deep[u]+MN);
    56                 if(deep[f]==deep[u]-tim[f]-MN) --ans[f];        
    57             }
    58         }
    59     }
    60 }//处理差分位置
    61 void solve(){
    62     dfs1(1);//进行第一次遍历统计答案
    63     for (register int i=1; i<=n; ++i) tim[i]+=MN<<1;//这个处理很关键!
    64     dfs2(1);
    65     for (register int i=1; i<n; ++i) printf("%d ",ans[i]); printf("%d",ans[n]);
    66 }
    67 int main(){
    68     read();
    69     init();
    70     solve();
    71 }
  • 相关阅读:
    C# 关于类的事件和委托
    C# 多态
    C# 声明方法的语法
    C# 面向对象基础
    近期发现的一些.net资源
    asp.net 2.0学习资源
    设置VSS使支持通过Internet访问
    大型社区设计:提高用户体验的10个细节
    委托的用法
    有滚动条、固定Header的ASP.Net DataGrid实现
  • 原文地址:https://www.cnblogs.com/Melacau/p/NOIP2016_running.html
Copyright © 2020-2023  润新知