usaco 2013 open && JZOJ 3234 阴阳

Description

给出一棵树。树的边权为1或-1。现在要求有多少个点对 (u,v) 满足,存在中间点 k(ku,v) dis[u,k]=0 dis[k,v]=0 dis[u,v] 表示从 u v 的最短路径上的边权和。

Analysis

求树上点对数的题目一般都用点分治解决。这题亦是如此,当作是点分治的练习。
这题大概要把合法点对分成三类:

  1. 路径经过根节点(其中,根节点作为中间点还要特殊考虑)
  2. 根节点为一条路径的端点
  3. 整个路径都在子树里面

第3点直接沿着子树递归下去即可。
第2点特判一下直接计入答案。关键是第一点。
首先把 dis[v] (表示 v 到当前重心的dis)和 p[v] (表示 dis[v] v 的祖先中是否出现过)一边dfs求出。
然后满足 p[v]=true 的点和其他点算路径时满足有合法的中间点在 (v,root) 上。基于这个思想,再分类讨论一下即可。这题的细节非常多,能一次 AC 的都是大神。

Key Code

    fo(i,1,num)
    {
        if(a[i].p && a[i].w==0) ans++;
        lyd[N+a[i].w][a[i].p]++;
    }
    ll x=0;
    top=a[num+1].bz=0;
    if(num)
    fo(i,1,num+1)
    {
        if(a[i].bz!=a[i-1].bz)
        {
            fo(k,1,top)
            {
                if(type[k]) x+=lyd[q[k]][0]+lyd[q[k]][1]-d[q[k]][0]-d[q[k]][1];
                else x+=lyd[q[k]][1]-d[q[k]][1];
            }
            for(;top;top--)
                d[q[top]][0]=d[q[top]][1]=d[N+N-q[top]][0]=d[N+N-q[top]][1]=0;
            if(i>num) break;
        }
        q[++top]=N-a[i].w,type[top]=a[i].p;
        d[N+a[i].w][a[i].p]++;
        if(a[i].w==0) type[top]=1;
    }
    ans+=x/2;

你可能感兴趣的:(USACO,点分治,树分治)