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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章