【FJOI2014[bzoj4016]】最短路径树问题

  • link to problem
  • 【题目大意】
    给出一张无向图,求图中字典序最小的最短路径树上最长的包含 k 个点的简单路径长度及方案数。其中最短路径树定义:从顶点 1 出发,往其余所有点分别走一次并返回。往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径。到达该点后按原路返回,然后往其他点走,直到所有点都走过。

  • 【题解】点分治+树形dp
    首先处理字典序最小的最短路径树。显然知道树上节点到顶点 1 距离最短,为了保证字典序将边排序,做个 spfa 求最短路(或者 dijkstra+heap 优化),再一遍 dfs 建树。
    接下来,依然按照点分治的基本思路将路径分为是否经过当前树根节点两类。
    考虑经过根节点情况。我们记 x 为根节点(重心):

    • f [i]:x为根节点树上已处理的节点到根 x 经过节点数为 i 的路径最大长度;
    • g[i]:f[i] 意义下对应的方案数;
    • f f [i]:当前处理的根为 son 的子树上节点到 son 经过节点数为 i 的路径最大长度;
    • gg[i]:ff[i] 意义下对应的方案数。//这名字是不是预示了我要gg的结果。。

    每一次我们先 bfs 处理 ff 和 gg数组。再用当前子树信息和之前存下的信息更新答案和方案数。做完以后将子树信息和其他信息合并。


  • 【细节注意】//具体看程序
    • 字典序最小:我处理的时候是把它倒序排这样存边倒着做回来就能保证从序号小开始做。。。然而打崩了好几次都是建树的问题QAQAQAQ….
    • 在做 dp 的过程中在从当前树找子树重心的时候,注意到由于之前乱搞可能当前子树 son 大小会比size[x]大???(什么鬼),所以如果不对就扣一下好了。。。
    • 数组清空:这个我真不知道什么鬼,越界访问这种神奇的东西。反正我是在做完了spfa之后dp数组都莫名有了初始值,然后清空一下竟然就对了。辣鸡题目还我AC率TAT… 不过还有要注意 dp 数组的清空就是了。

{ 总而言之树分治的细节总是很多,打和调试分不同过程想清楚就好啦 (⊙v⊙) }


【呆马 ( ⊙o⊙ )】

#include 
#include 
#include 
#include 
#define inf 1000000000
#define maxn 30005
struct edge{ int to,s,nxt;}e[maxn*4],e1[maxn*4];
struct edge1{ int u,v,w;}a[maxn*4];
int n,m,k,ans,sum,cnt,mx,tot,rt,fi[maxn],fi1[maxn],s[maxn],d[maxn],num[maxn],
    p[maxn],q[maxn*8],f[maxn],g[maxn],ff[maxn],gg[maxn];
bool bo[maxn];
    void add1(int u,int v,int w)
    {
        e1[++cnt].to=v;e1[cnt].s=w;
        e1[cnt].nxt=fi1[u];fi1[u]=cnt;
    }
    void add(int u,int v,int w)
    {
        e[++cnt].to=v;e[cnt].s=w;
        e[cnt].nxt=fi[u];fi[u]=cnt;
    }
    void spfa(int x)
    {
        int h,t;bo[1]=true;
        for (d[q[h=t=1]=x]=0;h<=t;bo[q[h++]]=false)
            for (int i=fi1[q[h]];i;i=e1[i].nxt)
                if (d[q[h]]+e1[i].sq[h]]+e1[i].s;
                    if (bo[e1[i].to]) continue;
                    bo[q[++t]=e1[i].to]=true;
                }
    }
    void dfs(int x)
    {
        bo[x]=true;
        for (int i=fi1[x];i;i=e1[i].nxt)
            if (!bo[e1[i].to] && d[x]+e1[i].s==d[e1[i].to])
            {
                add(x,e1[i].to,e1[i].s);
                add(e1[i].to,x,e1[i].s);
                dfs(e1[i].to);
            }
    }
    void findrt(int x,int fa)
    {
        s[x]=1;int mxx=0;
        for (int i=fi[x];i;i=e[i].nxt)
            if (e[i].to!=fa && !bo[e[i].to])
            {
                findrt(e[i].to,x);
                mxx=std::max(mxx,s[e[i].to]);
                s[x]+=s[e[i].to];
            }
        mxx=std::max(tot-s[x],mxx);
        if (mxxx;
    }
    void bfs(int x,int s)
    {
        int h,t;
        num[x]=1;d[x]=s;
        for (q[h=t=1]=x;h<=t && num[q[h]]<=k-1;++h)
        {
            for (int j=fi[q[h]];j;j=e[j].nxt)
                if (!bo[e[j].to] && e[j].to!=p[q[h]])
                {
                    num[e[j].to]=num[q[h]]+1;
                    d[e[j].to]=d[q[h]]+e[j].s;
                    p[q[++t]=e[j].to]=q[h];
                }
            if (d[q[h]]>ff[num[q[h]]]) 
                ff[num[q[h]]]=d[q[h]],gg[num[q[h]]]=1;
            else gg[num[q[h]]]+=(d[q[h]]==ff[num[q[h]]]);
        }
    }
    void dp(int x,int y)
    {
        bo[x]=true;g[0]=1;
        for (int i=fi[x];i;i=e[i].nxt)
            if (!bo[e[i].to])
            {
                p[e[i].to]=x;
                bfs(e[i].to,e[i].s);
                for (int j=1;jint w=ff[j]+f[k-j-1];
                    if (w>ans) ans=f[k-j-1]+ff[j],sum=g[k-j-1]*gg[j];
                    else sum+=(w==ans)*g[k-j-1]*gg[j];
                }
                for (int j=1;jif (ff[j]>f[j]) f[j]=ff[j],g[j]=gg[j];
                    else g[j]+=gg[j]*(ff[j]==f[j]);
                for (int j=1;j0;
            }
        for (int i=0;i0;
        for (int i=fi[x];i;i=e[i].nxt)
            if (!bo[e[i].to]) 
            {
                if (s[e[i].to]>s[x]) s[e[i].to]=y-s[x];
                rt=0;tot=mx=s[e[i].to];
                if (s[e[i].to]>=k-1) findrt(e[i].to,0);
                dp(rt,s[e[i].to]);
            }
    }
    bool comp(edge1 x,edge1 y){ return x.v>y.v;}
int main()
{
    scanf("%d%d%d\n",&n,&m,&k);
    for (int i=1;i<=m;++i)
    {
        int u,v,w;
        scanf("%d%d%d\n",&u,&v,&w);
        a[++tot].u=u;a[tot].v=v;a[tot].w=w;
        a[++tot].u=v;a[tot].v=u;a[tot].w=w;     
    }
    std::sort(a+1,a+tot+1,comp);
    for (int i=1;i<=tot;++i) add1(a[i].u,a[i].v,a[i].w);
    for (int i=2;i<=n;++i) d[i]=inf;
    spfa(1);cnt=0;dfs(1);
    memset(bo,false,sizeof(bo));
    rt=0;tot=mx=n;findrt(1,0);
    ans=sum=0;
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    memset(ff,0,sizeof(ff));
    memset(gg,0,sizeof(gg));
    dp(rt,n);
    printf("%d %d\n",ans,sum);
    return 0;
}

你可能感兴趣的:(【FJOI2014[bzoj4016]】最短路径树问题)