bzoj4719 [Noip2016]天天愛跑步

傳送門

終於把去年noip的坑填完了啊。。。好可怕。。。
這道題的做法是LCA+差分+桶。(其實只能算是半個差分)
首先我們要確定搜索對象。玩家的數量那麼多而且還在移動顯然不能成爲搜索對象。所以我們要搜索的是觀察員。
然後我們觀察題目,我們發現若當前搜索到點x,如果某個玩家的路徑經過x,那麼這個玩家的路徑起點或終點必然在x的子樹中。
然後我們要尋找性質來使得時間從動態轉化爲靜態。
我們發現可以將玩家的路徑拆分爲start到lca,再從lca到end。
若某個玩家的路徑起點在x的子樹中,我們可以發現,若這個玩家所在的位置爲i,出發time[i]的時間,那麼

time[i]=deep[start]deep[i]

移項得到
deep[i]+time[i]=deep[start]

我們可以發現等式左邊和右邊都是定值。
同樣,若某個玩家的路徑終點在x的子樹中,路徑長度爲len,那麼
deep[end]deep[i]=lentime[i]

將len拆解爲deep[start]+deep[end]2deep[lca] ,代入原式,化簡得到
deep[i]time[i]=2deep[lca]deep[start]

等式左邊和右邊同樣都是定值。
這樣我們就可以使用兩個桶,分別維護從start到lca,再從lca到end兩段,上述兩個等式右邊可以用作玩家在桶中使用的下標,等式左邊可以用作觀察員在桶中使用的下標。注意:第二個等式在使用的時候可能會出現負數,放到桶裏之前應該先加上一個常數。

CODE:

#include<cstdio>
#include<cstdlib>
const int N=3e5+10;
struct edge
{
    int nxt,to;
}a[N<<1];
struct Edge
{
    int nxt,to;
    bool tmp,dec;
}e[N<<1];
struct node
{
    int nxt,to;
    bool tmp;
}E[N<<1];
int head[N],Head[N],h[N],deep[N],f[N],size[N],son[N],top[N];
int w[N],t1[N<<1],t2[N<<1],ans[N];
int n,m,x,y,num,Num=1,tot,cnt;
inline void add(int x,int y)
{
    a[++num].nxt=head[x],a[num].to=y,head[x]=num;
    a[++num].nxt=head[y],a[num].to=x,head[y]=num;
}
inline void Add(int x,int y)
{
    e[++Num].nxt=Head[x],e[Num].to=y,e[Num].tmp=1,Head[x]=Num;
    e[++Num].nxt=Head[y],e[Num].to=x,e[Num].tmp=0,Head[y]=Num;
}
inline void insert(int x,int y,bool z)
{
    E[++cnt].nxt=h[x],E[cnt].to=y,E[cnt].tmp=z,h[x]=cnt;
}
void dfs(int now,int fa,int depth)
{
    f[now]=fa,deep[now]=depth;
    size[now]=1;
    int tmp=0;
    for(int i=head[now];i;i=a[i].nxt)
      if(a[i].to!=fa)
      {
        dfs(a[i].to,now,depth+1);
        size[now]+=size[a[i].to];
        if(size[a[i].to]>tmp) tmp=size[a[i].to],son[now]=a[i].to;
      }
}
void dfs2(int now,int high)
{
    top[now]=high;
    if(son[now]) dfs2(son[now],high);
    for(int i=head[now];i;i=a[i].nxt)
      if(a[i].to!=f[now]&&a[i].to!=son[now]) dfs2(a[i].to,a[i].to);
}
inline int LCA(int x,int y)
{
    while(top[x]!=top[y])
      if(deep[top[x]]>deep[top[y]]) x=f[top[x]];
      else y=f[top[y]];
    return deep[x]<deep[y]?x:y;
}
void solve(int now)
{
    int tmp1=t1[deep[now]+w[now]+N],tmp2=t2[deep[now]-w[now]+N];
    for(int i=Head[now];i;i=e[i].nxt)
    {
        int lca=LCA(now,e[i].to);
        if(!e[i].dec)
        {
            if(deep[lca]+w[lca]==deep[e[i].tmp?now:e[i].to]) ans[lca]--;
            e[i].dec=e[i^1].dec=1;
        }
        if(e[i].tmp) t1[deep[now]+N]++,insert(lca,now,0);
        else t2[(deep[lca]<<1)-deep[e[i].to]+N]++,insert(lca,e[i].to,1);
    }
    for(int i=head[now];i;i=a[i].nxt)
      if(a[i].to!=f[now]) solve(a[i].to);
    ans[now]+=t1[deep[now]+w[now]+N]-tmp1+t2[deep[now]-w[now]+N]-tmp2;
    for(int i=h[now];i;i=E[i].nxt)
      if(E[i].tmp) t2[(deep[now]<<1)-deep[E[i].to]+N]--;
      else t1[deep[E[i].to]+N]--;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
      scanf("%d%d",&x,&y),add(x,y);
    for(int i=1;i<=n;i++)
      scanf("%d",&w[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        if(x==y)
          if(!w[x]) ans[x]++;
          else;
        else Add(x,y);
    }
    dfs(1,0,1),dfs2(1,1);
    solve(1);
    for(int i=1;i<n;i++)
      printf("%d ",ans[i]);
    printf("%d",ans[n]);//bzoj卡最後一行的空格
    return 0;
}
發佈了137 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章