洛谷P1600 天天爱跑步

题目:https://www.luogu.org/problem/P1600

【转】https://www.cnblogs.com/lfyzoi/p/10221884.html侵删

结合上面别人写的博客,我写了下面的一些解释。

本文对lca不作讲解,重点讲解如何树上差分及用桶的思想求出答案。

【要点一】

对于观察员p, 如果他位于一条起点、终点分别为 si, ti 的跑步路径上,如何判断选手 i 可以被观察员p看到?

1)观察员p在 si 到lca的链上,则满足deep[si]=deep[p]+w[p]时将被看到!所以,引入上行桶的概念,对于观察员而言,他应该去查询上行桶tong1 [deep[p]+w[p] ]里的值。(这里实际上查的是值的增量!后面解释)

2)观察员p在lca到 ti 的链上,则满足dist(si,ti)-w[p]=deep[ti]-deep[p]即dist(si,ti)-deep[ti]=w[p]-deep[p]时将被看到!所以,引入下行桶的概念,对于观察员p而言,他还应该去查询下行桶tong2 [ w[p]-deep[p] ]里的值。(与前面一样,查的是值的增量。另外,为防下标下溢,写成tong2 [ w[p]-deep[p]+SIZE ])

3)在上行链时,不考虑玩家路径的终点ti。在下行链时,不考虑玩家路径的起点si。

【要点二】

树上差分

1)记录以si为起点的路径数目为js[si],则在上行桶tong1[ deep[si] ]+=js[si]——这就与树上差分拉上关系了。

如果si不是任何跑步路径的起点,则js[si]=0,从而等价于tong[ deep[si] ]+=js[si]的值没有改变。

2)对于ti而言,如果ti是某条跑步路径的终点,则在下行桶tong2[ dist(si,ti)-deep[ti] ]++  ——又与树上差分拉上关系了。

如果ti不是任何跑步路径的终点,则下行桶tong2[ dist(si,ti)-deep[ti] ]不用加1。

3)dfs回溯,需要将当前条的跑步路径的起点加在tong1的值--,终点加在tong2的值--。

【要点三】

观察员p的观察到的人数ans[p]=tong1[ deep[p]+w[p] ]的增量值+tong2[ w[p]-deep[p] ]的增量值。

为什么时增量值呢?因为这些增量要么是起点提供的,要么是终点提供的。

代码(转自上面别人的博客):

#include
using namespace std;
const int SIZE=300000;
int n, m, tot, h[SIZE], deep[SIZE], fa[SIZE][20], w[SIZE];      //w[i]表示i结点出现观察员的时间
struct edge
{
    int to, next;
}E[SIZE*2], e1[SIZE*2], e2[SIZE*2];                             //边集数组e1,e2留待备用

void add(int x, int y)                                          //加边函数
{
    E[++tot].to=y;
    E[tot].next=h[x];
    h[x]=tot;
}

void dfs1(int x)                                                //dfs的过程中完成“建树”,预处理fa[][]数组, 计算deep[]数组
{
    for(int i=1; (1<=0; i--)                                    //x向上跳到和y同样的深度
    {
        if(deep[fa[x][i]]>=deep[y])
            x=fa[x][i];
        if(x==y)
            return x;
    }
    t=log(deep[x])/log(2);
    for(int i=t; i>=0; i--)                                    //x和y一起向上跳
    {
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i], y=fa[y][i];
    }
    return fa[x][0];
}

int tot1, tot2, h1[SIZE], h2[SIZE];
void add1(int x, int y)
{
    e1[++tot1].to=y;
    e1[tot1].next=h1[x];
    h1[x]=tot1;
}

void add2(int x, int y)
{
    e2[++tot2].to=y;
    e2[tot2].next=h2[x];
    h2[x]=tot2;
}

int b1[SIZE*2], b2[SIZE*2], js[SIZE], dist[SIZE], s[SIZE], t[SIZE], ans[SIZE];

void dfs2(int x)
{
    int t1=b1[w[x]+deep[x]], t2=b2[w[x]-deep[x]+SIZE];      //递归前先读桶里的数值,t1是上行桶里的值,t2是下行桶的值
    for(int i=h[x]; i; i=E[i].next)                         //递归子树
    {
        int y=E[i].to;
        if(y==fa[x][0]) continue;
        dfs2(y);
    }
    b1[deep[x]]+=js[x];                                     //上行过程中,当前点作为路径起点产生贡献,入桶
    for(int i=h1[x]; i; i=e1[i].next)                       //下行过程中,当前点作为路径终点产生贡献,入桶
    {
        int y=e1[i].to;
        b2[dist[y]-deep[t[y]]+SIZE]++;
    }
    ans[x]+=b1[w[x]+deep[x]]-t1+b2[w[x]-deep[x]+SIZE]-t2;   //计算上、下行桶内差值,累加到ans[x]里面
    for(int i=h2[x]; i; i=e2[i].next)                       //回溯前清除以此结点为LCA的起点和终点在桶内产生的贡献,它们已经无效了
    {
        int y=e2[i].to;
        b1[deep[s[y]]]--;                                   //清除起点产生的贡献
        b2[dist[y]-deep[t[y]]+SIZE]--;                      //清除终点产生的贡献
    }
}

int main()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d", &n, &m);
    for(int i=1; i

 

 

 

你可能感兴趣的:(洛谷P1600 天天爱跑步)