bzoj 4016: [FJOI2014]最短路径树问题 最短路+点分治

来源:互联网 发布:唐安琪最新知乎 编辑:程序博客网 时间:2024/06/05 19:09

题意

给一个包含n个点,m条边的无向连通图。从顶点1出发,往其余所有点分别走一次并返回。
往某一个点走时,选择总长度最短的路径走。若有多条长度最短的路径,则选择经过的顶点序列字典序最小的那条路径(如路径A为1,32,11,路径B为1,3,2,11,路径B字典序较小。注意是序列的字典序的最小,而非路径中节点编号相连的字符串字典序最小)。到达该点后按原路返回,然后往其他点走,直到所有点都走过。
可以知道,经过的边会构成一棵最短路径树。请问,在这棵最短路径树上,最长的包含K个点的简单路径长度为多长?长度为该最长长度的不同路径有多少条?
这里的简单路径是指:对于一个点最多只经过一次的路径。不同路径是指路径两端端点至少有一个不同,点A到点B的路径和点B到点A视为同一条路径。
n<=30000,m<=60000,2<=K<=n。

分析

首先要把最短路树求出来。这个的话可以先把原图的最短路DAG求出来,然后从1开始,每次沿着字典序最小的边跑,就可以得到最短路树。
那后就是个sb点分治了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N=30005;const int inf=1000000000;int n,m,k,cnt,last[N],tot,ans,ans1,dis[N],now,t[N],size[N],mx[N],root,s[N];bool vis[N];pair<int,int> a[N];pair<int,pair<int,int> > tmp[N*2];struct edge{int to,next,w,use;}e[N*4];priority_queue<pair<int,int> > que;int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}void addedge(int u,int v,int w){    e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].to=u;e[cnt].w=w;e[cnt].next=last[v];last[v]=cnt;}void dfs(int x){    vis[x]=1;    for (int i=last[x];i;i=e[i].next)        if (e[i].use==1&&!vis[e[i].to]) e[i].use=e[i^1].use=2,dfs(e[i].to);}void dij(){    for (int i=2;i<=n;i++) dis[i]=inf;    que.push(make_pair(0,1));    while (!que.empty())    {        pair<int,int> u=que.top();que.pop();        while (!que.empty()&&vis[u.second]) u=que.top(),que.pop();        if (vis[u.second]) break;        int x=u.second;vis[x]=1;        for (int i=last[x];i;i=e[i].next)            if (!vis[e[i].to]&&dis[x]+e[i].w<dis[e[i].to])            {                dis[e[i].to]=dis[x]+e[i].w;                que.push(make_pair(-dis[e[i].to],e[i].to));            }    }    for (int i=2;i<=cnt;i++)    {        int x=e[i^1].to,y=e[i].to;        if (dis[x]+e[i].w==dis[y]) e[i].use=1;    }    memset(vis,0,sizeof(vis));    dfs(1);}void get_root(int x,int fa){    size[x]=1;mx[x]=0;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].use<2||vis[e[i].to]||e[i].to==fa) continue;        get_root(e[i].to,x);        size[x]+=size[e[i].to];        mx[x]=max(mx[x],size[e[i].to]);    }    mx[x]=max(mx[x],tot-size[x]);    if (!root||mx[x]<mx[root]) root=x;}void get_w(int x,int len,int dis,int fa){    if (len<=k) a[++now]=make_pair(dis,len);    size[x]=1;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].use<2||vis[e[i].to]||e[i].to==fa) continue;        get_w(e[i].to,len+1,dis+e[i].w,x);        size[x]+=size[e[i].to];    }}void solve(int x){    vis[x]=1;    now=0;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].use<2||vis[e[i].to]) continue;        int tmp=now;        get_w(e[i].to,1,e[i].w,x);        for (int j=tmp+1;j<=now;j++)        {            int w=a[j].first,len=a[j].second;            if (t[k-len]>-1)            {                if (w+t[k-len]>ans) ans=w+t[k-len],ans1=s[k-len];                else if (w+t[k-len]==ans) ans1+=s[k-len];            }        }        for (int j=tmp+1;j<=now;j++)        {            int w=a[j].first,len=a[j].second;            if (w>t[len]) t[len]=w,s[len]=1;            else if (w==t[len]) s[len]++;        }    }    while (now) t[a[now].second]=-1,s[a[now].second]=0,now--;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].use<2||vis[e[i].to]) continue;        root=0;tot=size[e[i].to];        get_root(e[i].to,x);        solve(root);    }}int main(){    n=read();m=read();k=read()-1;cnt=1;    for (int i=1;i<=m;i++)    {        int x=read(),y=read(),z=read();        tmp[i]=make_pair(x,make_pair(y,z));    }    sort(tmp+1,tmp+m+1);    for (int i=m;i>=1;i--) addedge(tmp[i].first,tmp[i].second.first,tmp[i].second.second);    dij();    memset(vis,0,sizeof(vis));    tot=n;root=0;    get_root(1,0);    for (int i=1;i<=k;i++) t[i]=-1;    s[0]=1;    solve(root);    printf("%d %d",ans,ans1);    return 0;}
原创粉丝点击