NOIP2016 天天愛跑步 正解


暴力移步 http://www.cnblogs.com/TheRoadToTheGold/p/6673430.html

 

 

首先解決本題應用的知識點:

dfs序——將求子樹的信息(樹形)轉化為求一段連續區間信息(線形)

線段樹——求區間信息

樹上差分——統計答案

lca——拆分路徑

樹鏈剖分——求lca

 

另deep[]表示節點的深度,watch[]表示觀察者的出現時間,s表示玩家起點,t表示終點

固定節點的觀察者出現的時間固定,說明對這個觀察者有貢獻的點是有限且固定的

只有滿足  觀察者出現時間=玩家起點與觀察者距離的  玩家才對觀察者有貢獻

 

每條路徑拆成   起點到lca(向上跑)  和   終點到lca的子節點(向下跑)  的兩條路徑

對於向上跑的,如果玩家能被觀察員i觀察到,那么deep[s]-deep[i]=watch[i]   式①

對於向下跑的,就是 deep[s]+deep[i]-2*deep[lca(s,i)]=watch[i]  式②

等號左邊是玩家起點與觀察者的距離,等號右邊是觀察者出現時間

向上跑的很顯然,向下跑的如何理解?

假設我們知道點a,b到lca(a,b)的距離分別為da,db,那么a,b之間的距離=da+db

但這里的deep不是到lca的距離,是深度,即到根節點的距離+1

deep[s]+deep[i]包含2段信息,1、s到i的距離,   2、lca(s,i)到根節點的距離+1 

第2段包含了2次,所以減去

 

先看向上跑的

玩家路徑:玩家起點 到 起點與終點的lca

將式①移項,deep[s]=deep[i]+watch[i]

發現等號右邊是定值

也就是說對與觀察者i,他所能觀察到的向上跑的玩家,是所有的起點=deep[i]+watch[i]的玩家

換句話說,以i為根的子樹中,所有深度為deep[i]+watch[i]的玩家都能被i觀察到

我們如果搞一個dfs序,i的在a時入棧,在b時出棧,

那么以i為根的子樹就可以轉化為區間[a,b]

深度咋整?

我們對每個深度建立一顆線段樹(動態加點)

那么問題就轉化為了  在深度為deep[i]+watch[i]的線段樹中,查詢區間[a,b]的玩家個數

現在就差玩家個數了

很容易想到在起點處+1

但是還要在起點與終點的lca的父節點處-1

差分慣用思想

用sum[]統計這些1和-1的和

那么問題就轉化為了  在深度為deep[i]+watch[i]的線段樹中,查詢區間[a,b]的sum和

 

提問:為什么是起點處+1,lca的父節點處-1,可以反過來嗎?

不可以。

因為起點的深度深,lca的父節點深度淺,在深度深的節點處+1,以深度深度淺的點為根的子樹可以包含這個點

想想序列上的差分,是左端點+1,右端點后面的點-1

因為序列差分與前綴和相聯系,前面的點的信息對后面的點會產生影響,所以只需加一個1

這里查詢的是子樹信息,是這個點深度及以下的信息

對照理解即可

 

向下跑的同理,只簡單說怎么做

玩家路徑:lca的子節點到玩家終點

把式②移項 deep[s]-2*deep[lca(s,i)]=watch[i]-deep[i]

在watch[i]-deep[i]深度為deep[s]-2*deep[lca(s,i)]的線段樹中,終點處+1,lca處-1

查詢時查深度為watch[i]-deep[i]的線段樹即可

 

2個小問題:

1、做完向上跑的后,不要忘了清空線段樹

2、向下跑的deep[s]-2*deep[lca(s,i)]可能會產生負數,所以全體后移一定長度,root[]數組開大

我后移了2*n,那么root[]數組要開3倍

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300401
using namespace std;
int n,m,fa[N],son[N],deep[N],bl[N],sz,id[N],ans[N];
int in[N],out[N],watch[N];
int front[N],nextt[N*2],to[N*2];
int root[N*3],lc[N*25],rc[N*25],sum[N*25],tot,cnt;
struct node
{
    int s,t,lca;
}runner[N];
void add(int u,int v)
{
    to[++cnt]=v; nextt[cnt]=front[u]; front[u]=cnt;
}
void dfs1(int now)
{
    son[now]++;
    for(int i=front[now];i;i=nextt[i])
    {
        if(to[i]==fa[now]) continue;
        deep[to[i]]=deep[now]+1;
        fa[to[i]]=now;
        dfs1(to[i]);
        son[now]+=son[to[i]];
    }
}
void dfs2(int now,int chain)
{
    id[now]=++sz;
    in[now]=sz;
    bl[now]=chain;
    int y=0;
    for(int i=front[now];i;i=nextt[i])
    {
        if(to[i]==fa[now]) continue;
        if(son[to[i]]>son[y]) y=to[i];
    }
    if(!y) 
    {
        out[now]=sz;
        return;
    }
    dfs2(y,chain);
    for(int i=front[now];i;i=nextt[i])
    {
        if(to[i]==fa[now]||to[i]==y) continue;
        dfs2(to[i],to[i]);
     }
     out[now]=sz;
}
int getlca(int u,int v)
{
    while(bl[u]!=bl[v])
    {
        if(deep[bl[u]]<deep[bl[v]]) swap(u,v);
        u=fa[bl[u]];
    }
    return deep[u]<deep[v] ? u:v;
}
void change(int & now,int l,int r,int pos,int w)
{
    if(!pos) return;
    if(!now) now=++tot;
    sum[now]+=w;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) change(lc[now],l,mid,pos,w);
    else change(rc[now],mid+1,r,pos,w);
}
int query(int now,int l,int r,int opl,int opr)
{
    if(!now) return 0;
    if(l==opl&&r==opr) return sum[now];
    int mid=l+r>>1;
    if(opr<=mid) return query(lc[now],l,mid,opl,opr);
    else if(opl>mid) return query(rc[now],mid+1,r,opl,opr);
    else return query(lc[now],l,mid,opl,mid)+query(rc[now],mid+1,r,mid+1,opr);
}
void clear()
{
    tot=0;
    memset(lc,0,sizeof(lc));
    memset(rc,0,sizeof(rc));
    memset(sum,0,sizeof(sum));
    memset(root,0,sizeof(root));
}
int main()
{
    /*freopen("runninga.in","r",stdin);
    freopen("runninga.out","w",stdout);*/
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++) scanf("%d",&watch[i]);
    for(int i=1;i<=m;i++) scanf("%d%d",&runner[i].s,&runner[i].t);
    dfs1(1);
    dfs2(1,0);
    for(int i=1;i<=m;i++) runner[i].lca=getlca(runner[i].s,runner[i].t);
    int now;
    for(int i=1;i<=m;i++)
    {
        now=deep[runner[i].s];
        change(root[now],1,n,id[runner[i].s],1);
        change(root[now],1,n,id[fa[runner[i].lca]],-1);
    }
    for(int i=1;i<=n;i++) ans[i]=query(root[deep[i]+watch[i]],1,n,in[i],out[i]);
    clear();
    for(int i=1;i<=m;i++) 
    {
        now=deep[runner[i].s]-deep[runner[i].lca]*2+n*2;
        change(root[now],1,n,id[runner[i].t],1);
        change(root[now],1,n,id[runner[i].lca],-1);
    }
    for(int i=1;i<=n;i++) ans[i]+=query(root[watch[i]-deep[i]+n*2],1,n,in[i],out[i]);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM