[bzoj 4016] [Fjoi 2014] 最短路径树问题

题目在这里

有生之年参加的第一次省选,当时太年轻太弱又碰到3道丧病题果断爆零了。(虽然现在还是很弱)考场上以为这是一道树形dp,因为树上的题目除了dfs和dp外啥都不知道。。。。。

这题是点分治大裸题(我也只会写大裸题),然而我建树写了两节课。。。大致就是先从1号点跑一遍spfa或heap-dijkstra,显然满足dis[x]+w[x][y]==dis[y]的边在最短路径上。由于要求路径字典序最小,就优先走编号小的节点,这样一个点第一次被dfs到就一定是1到它的路径字典序最小了。

建完树之后直接点分治。记一下每棵子树的到重心刚好经过x个节点的路径的最大值_max[x],在记录一下这个最大值出现多少次,然后无脑码个150行注意一下就A了。

//最短路树+点分治 记录方案数 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <ctime>
using namespace std;

const int MAXN=30005,MAXM=60005,INF=1000000005;
struct Edge
{ 
    int pre,from,to,cost,pos; bool flag;
}E[MAXM*2],tmp[MAXM*2];
int n,m,K,tot,fst[MAXN],dis[MAXN],depth[MAXN],size[MAXN],maxsize[MAXN],w[MAXN],_max[MAXN],nowmax[MAXN],st,ed,
nowroot,cnt,amo[MAXN],nowamo[MAXN],son[MAXN],ans,sum;

bool vis[MAXN]; 

struct Node
{ 
    int x,val;
    Node () {} Node(int _x,int _val) { x=_x,val=_val; }  
}; 
priority_queue<Node> H; 
bool operator < (Node a,Node b) { return a.val>b.val; }
bool cmp1(const Edge &a,const Edge &b) { return (a.from<b.from) || (a.from==b.from && a.to<b.to); }
bool cmp2(const Edge &a,const Edge &b) { return a.pos<b.pos; }

int Get()
{ 
    char ch; int v=0; bool f=false;
    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
    while (isdigit(ch=getchar())) v=v*10+ch-48;
    if (f) return -v;else return v;   
}

void add(int x,int y,int c,bool f)
{ 
    E[++tot].pre=fst[x],fst[x]=tot,E[tot].from=x,E[tot].to=y,E[tot].cost=c,E[tot].flag=false;
    if (f) E[++tot].pre=fst[y],fst[y]=tot,E[tot].from=y,E[tot].to=x,E[tot].cost=c,E[tot].flag=false;
}

void Dijkstra() 
{ 
    for (int i=1;i<=n;i++) dis[i]=INF,vis[i]=false; dis[1]=0,H.push(Node(1,dis[1])),vis[1]=true;
    while (!H.empty())
      {  
        Node tmp=H.top(); H.pop(); 
        int x=tmp.x; if (tmp.val!=dis[x]) continue; vis[x]=true; 
        for (int i=fst[x];i;i=E[i].pre)
          { 
            int y=E[i].to;
            if (!vis[y] && dis[x]+E[i].cost<dis[y]) dis[y]=dis[x]+E[i].cost,H.push(Node(y,dis[y]));
          }
      }
}

void dfs_init(int x) //建树 
{ 
    vis[x]=true; 
    for (int i=fst[x];i;i=E[i].pre)
      { 
        int y=E[i].to; 
        if (!vis[y] && dis[x]+E[i].cost==dis[y]) depth[y]=depth[x]+1,E[i].flag=true,dfs_init(y); 
      }
}

void Build_Tree()
{ 
    for (int i=1;i<=tot;i++) tmp[i].from=E[i].from,tmp[i].to=E[i].to,tmp[i].cost=E[i].cost,tmp[i].pos=i;
    sort(tmp+1,tmp+1+tot,cmp1); for (int i=1;i<=n;i++) vis[i]=false,fst[i]=0; int ttot=tot; tot=0; 
    for (int i=ttot;i>=1;i--) add(tmp[i].from,tmp[i].to,tmp[i].cost,0); 
    depth[1]=1; dfs_init(1); 

    for (int i=1;i<=tot;i++) tmp[i].from=E[i].from,tmp[i].to=E[i].to,tmp[i].cost=E[i].cost,tmp[i].flag=E[i].flag;
    for (int i=1;i<=n;i++) fst[i]=0,vis[i]=false; ttot=tot; tot=0; 
    for (int i=1;i<=ttot;i++) if (tmp[i].flag) add(tmp[i].from,tmp[i].to,tmp[i].cost,1); maxsize[0]=MAXN; 
} 

void dfs_size(int x,int fa) //算出孩子数 
{  
    maxsize[x]=0,size[x]=1,cnt++; 
    for (int i=fst[x];i;i=E[i].pre)
      { 
        int y=E[i].to;
        if (y!=fa && !vis[y]) dfs_size(y,x),size[x]+=size[y],maxsize[x]=max(maxsize[x],size[y]); 
      }
}

void dfs_root(int x,int fa) //找重心 
{  
    maxsize[x]=max(maxsize[x],cnt-size[x]); 
    if (maxsize[x]<maxsize[nowroot]) nowroot=x; 
    for (int i=fst[x];i;i=E[i].pre)
      { 
        int y=E[i].to;
        if (y!=fa && !vis[y]) dfs_root(y,x); 
      }
}

void modify(int &a,int b,int &c,int v) { if (b>a) a=b,c=v;else if (a==b) c+=v; }
void dfs_work(int x,int fa) //暴力统计 
{ 
    if (depth[x]>K) return; 
    son[++tot]=x; int tmp=_max[K-depth[x]]+w[x];   
    modify(nowmax[depth[x]],w[x],nowamo[depth[x]],1); modify(ans,tmp,sum,amo[K-depth[x]]); 
    for (int i=fst[x];i;i=E[i].pre)
      { 
        int y=E[i].to; 
        if (y!=fa && !vis[y]) w[y]=w[x]+E[i].cost,depth[y]=depth[x]+1,dfs_work(y,x); 
      }
} 

void work(int root) //计算路径经过x的答案 
{ 
    vis[root]=true; w[root]=0,depth[root]=0; tot=0; 
    for (int i=fst[root];i;i=E[i].pre) 
      { 
        int y=E[i].to;
        if (!vis[y]) 
          {
            st=tot+1,depth[y]=1,w[y]=E[i].cost,dfs_work(y,-1),ed=tot; 
            for (int j=st;j<=ed;j++) 
              modify(_max[depth[son[j]]],nowmax[depth[son[j]]],amo[depth[son[j]]],nowamo[depth[son[j]]]),
              nowmax[depth[son[j]]]=0,nowamo[depth[son[j]]]=0; 
          } 
      } 

    for (int i=1;i<=tot;i++) 
      { 
        if (depth[son[i]]==K) modify(ans,w[son[i]],sum,1);
        nowmax[depth[son[i]]]=_max[depth[son[i]]]=amo[depth[son[i]]]=nowamo[depth[son[i]]]=0;
      }
}

void dfs(int x,int fa) //最外层dfs 
{  
    cnt=nowroot=0; int root=0; dfs_size(x,fa);
    dfs_root(x,fa); root=nowroot;  
    work(root);  
    for (int i=fst[root];i;i=E[i].pre)
      {  
        int y=E[i].to; 
        if (!vis[y]) dfs(y,root);  
      }
}

int main()
{ 
    freopen("mindistree.in","r",stdin);
    freopen("mindistree.out","w",stdout);
    n=Get(),m=Get(),K=Get(); K--; int u,v,d; tot=0; 
    for (int i=1;i<=m;i++) u=Get(),v=Get(),d=Get(),add(u,v,d,1);
    Dijkstra(); 
    Build_Tree();
    ans=sum=0;  
    dfs(1,-1); 
    printf("%d %d\n",ans,sum);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值