cogs2557 天天爱跑步 LCA

Posted Loser Of Life

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cogs2557 天天爱跑步 LCA相关的知识,希望对你有一定的参考价值。

从去年$11$月填到现在的超级天坑……链接:http://cogs.pro/cogs/problem/problem.php?pid=2557

题意……给出一棵树,上面有一堆链,求出每个节点在指定的时刻有几条链恰好到达……

联赛超纲的开始……

详细叙述一下从去年十一月到现在的心路历程……

$2016.11$现场:卧槽这题一脸不可做啊……

$2016.12$:这个题出错地方了吧……

$2017.1$:什么玩意……好像是图上的吧……

$2017.2$(此时刚刚学习树):我好像会点了……(会什么?)如何建树……

$2017.3$:……前$25$分好像可做了?……

$2017.4$:卧槽我真是思博,链的数据都看不出来……

$2017.5$(学了树剖):这个东西树剖可做?……

$2017.6$:树剖确实可做……但是……一联赛不考,二修改些什么呢……

$2017.7$(听$ysf$讲树上差分):大概知道怎么修改了……待我推一推式子……

$2017.8$:噫!好!我过了!

不扯了好好写题解……

首先,每个人跑的路径$S->T$可以被拆分为两段:一段是从$S$到$LCA(S,T)$的向上阶段,另一段是从$LCA(S,T)$到$T$的向下阶段。接下来,我们分开进行考虑。

首先我们考虑向上阶段。一个人被某个点观察员看到,必要条件是他经过那个点(废话),充要条件是$deep[S]-time[i]=deep[i]$,其中,$time[i]$是该观察员观察的时间。

所以这个玩家一定是在这个点的子树之中(废话),那么我们搞一个$dfs$序,这个东西就被打成了区间问题。

接下来的问题就变成了:如何查询这个区间内出现了多少人。

这个解决方法可以说清奇古怪:每一层建一棵线段树(人傻自带一个$log$)。似乎看到了内存炸飞的景象?所以我们丢掉这些,动态开点……那么问题就变成了在$deep[i]+time[i]$所在这个子树上从进入这棵树到出这棵树有几个人的问题。

但是一个更大的难点又来了,怎么修改数目……

这就是我$7$月份才有了些许思路的原因了:听了$ysf$讲了树上差分(%%%$ysf$),意识到这条路径上全部增加可以差分为该点$+1$s,终点的下一个节点$-1$s。然后我们就直接在深度为$deep[S]$的线段树上操作、查询就可以了。

向下路径同理,只不过式子变成了$deep[S]-2*deep[LCA(S,T)]=time[i]-deep[i]$。由于等号左边可能为负,所以我们直接右移$2*n$位进行查询。也正因为如此,向上修改完成之后需要立刻查询,然后重建线段树,否则会发生混淆。

代码拿去吧……真的是乱七八糟……(话说最后我也没有用树剖诶……)

技术分享
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn=300005;
  7 struct node
  8 {
  9     int from,to,next;
 10 }edge[maxn<<1];
 11 int head[maxn],tot;
 12 void addedge(int u,int v)
 13 {
 14     edge[++tot]=(node){u,v,head[u]};head[u]=tot;
 15 }
 16 int n,tim[maxn],dep[maxn],rt[maxn][20],m,dfn[maxn],cnt,las[maxn];
 17 void dfs(int root,int pa,int depth)
 18 {
 19     dfn[root]=++cnt;rt[root][0]=pa;dep[root]=depth;
 20     for(int i=head[root];i;i=edge[i].next)
 21     {
 22         int v=edge[i].to;
 23         if(rt[root][0]!=v)dfs(v,root,depth+1);
 24     }
 25     las[root]=cnt;
 26 }
 27 void init()
 28 {
 29     for(int i=1;(1<<i)<=n;i++)
 30         for(int j=1;j<=n;j++)rt[j][i]=-1;
 31     for(int j=1;(1<<j)<=n;j++)
 32         for(int i=1;i<=n;i++)
 33             if(rt[i][j-1]!=-1)rt[i][j]=rt[rt[i][j-1]][j-1];
 34 }
 35 int lca(int x,int y)
 36 {
 37     int i;
 38     if(dep[x]<dep[y])swap(x,y);
 39     for(i=0;(1<<i)<=dep[x];i++);i--;
 40     for(int j=i;j>=0;j--)
 41         if(dep[x]-(1<<j)>=dep[y])x=rt[x][j];
 42     if(x==y)return y;
 43     for(int j=i;j>=0;j--)
 44         if(rt[x][j]!=-1&&rt[x][j]!=rt[y][j])x=rt[x][j],y=rt[y][j];
 45     return rt[x][0];
 46 }
 47 struct chara
 48 {
 49     int source,target,lca;
 50 }people[maxn];
 51 int sm[7500005],root[maxn<<2],lc[7500005],rc[7500005],num;
 52 #define mid ((l+r)>>1)
 53 #define lson lc[now],l,mid
 54 #define rson rc[now],mid+1,r
 55 void clear()
 56 {
 57     memset(sm,0,sizeof(sm));memset(root,0,sizeof(root));
 58     memset(lc,0,sizeof(lc));memset(rc,0,sizeof(rc));num=0;
 59 }
 60 void update(int &now,int l,int r,int pos,int val)
 61 {
 62     if(!pos)return;
 63     if(!now)now=++num;
 64     sm[now]+=val;
 65     if(l==r)return;
 66     if(pos<=mid)update(lson,pos,val);
 67     else update(rson,pos,val);
 68 }
 69 int ans[maxn];
 70 int query(int &now,int l,int r,int L,int R)
 71 {
 72     if(!now)return 0;
 73     if(L<=l&&r<=R)return sm[now];
 74     if(R<=mid)return query(lson,L,R);
 75     else if(L>mid)return query(rson,L,R);
 76     else return query(lson,L,mid)+query(rson,mid+1,R);
 77 }
 78 int haha()
 79 {
 80     freopen("runninga.in","r",stdin);
 81     freopen("runninga.out","w",stdout);
 82     scanf("%d%d",&n,&m);
 83     for(int i=1;i<n;i++)
 84     {
 85         int x,y;scanf("%d%d",&x,&y);
 86         addedge(x,y);addedge(y,x);
 87     }
 88     dfs(1,-1,1);init();
 89     for(int i=1;i<=n;i++)scanf("%d",&tim[i]);
 90     for(int i=1;i<=m;i++)
 91     {
 92         scanf("%d%d",&people[i].source,&people[i].target);
 93         people[i].lca=lca(people[i].source,people[i].target);
 94     }
 95     for(int i=1;i<=m;i++)
 96     {
 97         int deep=dep[people[i].source];
 98         update(root[deep],1,n,dfn[people[i].source],1);
 99         update(root[deep],1,n,dfn[rt[people[i].lca][0]],-1);
100     }
101     for(int i=1;i<=n;i++)ans[i]+=query(root[dep[i]+tim[i]],1,n,dfn[i],las[i]);
102     clear();
103     for(int i=1;i<=m;i++)
104     {
105         int deep=dep[people[i].source]-(dep[people[i].lca]<<1)+(n<<1);
106         update(root[deep],1,n,dfn[people[i].target],1);
107         update(root[deep],1,n,dfn[people[i].lca],-1);
108     }
109     for(int i=1;i<=n;i++)ans[i]+=query(root[tim[i]-dep[i]+(n<<1)],1,n,dfn[i],las[i]);
110     for(int i=1;i<=n;i++)printf("%d ",ans[i]);
111 }
112 int sb=haha();
113 int main(){;}
cogs2557

 

以上是关于cogs2557 天天爱跑步 LCA的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2016天天爱跑步

[luogu]P1600 天天爱跑步[LCA]

LCA+线段树 NOIP2016 天天爱跑步

luogu P1600 天天爱跑步 |树上差分+LCA

Luogu P1600 天天爱跑步 树上差分

LCA+树上差分天天爱跑步