Bzoj4016

Description

給一個包含n個點,m條邊的無向連通圖。從頂點1出發,往其餘所有點分別走一次並返回。
往某一個點走時,選擇總長度最短的路徑走。若有多條長度最短的路徑,則選擇經過的頂點序列字典序最小的那條路徑(如路徑A爲1,32,11,路徑B爲1,3,2,11,路徑B字典序較小。注意是序列的字典序的最小,而非路徑中節點編號相連的字符串字典序最小)。到達該點後按原路返回,然後往其他點走,直到所有點都走過。
可以知道,經過的邊會構成一棵最短路徑樹。請問,在這棵最短路徑樹上,最長的包含K個點的簡單路徑長度爲多長?長度爲該最長長度的不同路徑有多少條?
這裏的簡單路徑是指:對於一個點最多只經過一次的路徑。不同路徑是指路徑兩端端點至少有一個不同,點A到點B的路徑和點B到點A視爲同一條路徑。


題意:如上

解:先按字典序建出最短路樹,然後點分治,算出過當前點長爲k的簡單路徑的最大長度和數量。具體通過bfs得出當前點當前子樹下包含k個點及以內的最大長度和數量,然後與當前點之前計算完的子樹進行計算,得出到當前子樹的最大長度和數量。然後遞歸下去。

        還有一個小小的剪枝,是噹噹前點的size小於k時直接return,因爲此時一定無法更新答案,然後我的代碼從1084ms降到了648ms。

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<vector>
#define Rep(i,u) for(register int i=Begin[u],v=to[i];i;i=Next[i],v=to[i])
#define For(i,j,k) for(register int i=(j);i<=(int)k;i++)
#define Forr(i,j,k) for(register int i=(j);i>=(int)k;i--)
#define Set(a,b) memset((a),(b),sizeof(a)); 
#define pb push_back
#define ll unsigned long long
using namespace std;
const int N=30010,M=60010,INF=0x3f3f3f3f;
int Begin[N],to[M],Next[M],w[M],p[N],fa[N],siz[N],n,k,m,e=1,dis[N],dep[N],d[N],tmp[N][2],f[N][2];
struct edge{
    int v,w;
    edge(int v=0,int w=0):v(v),w(w){}
    bool operator <(const edge &r)const{
        return v<r.v;
    }
};
vector<edge>G[N];
queue<int>q;
inline void add(int x,int y,int z){
    to[++e]=y,Next[e]=Begin[x],Begin[x]=e,w[e]=z;
}
void read(int &x){
    x=0;char c=getchar();int f=0;
    while(c<'0'||c>'9'){c=getchar();if(c=='-')f=1;}
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();if (f)x=-x;
}
void spfa(int st){
    Set(d,INF);
    q.push(st),d[st]=0,p[st]=1;
    while(!q.empty()){
        int u=q.front(),S=G[u].size();q.pop();p[u]=0;
        For(i,0,S-1){
            int v=G[u][i].v;
            if (d[v]>d[u]+G[u][i].w){
                d[v]=d[u]+G[u][i].w;
                if(!p[v])p[v]=1,q.push(v);
            }
        }
    }   
}
void gettree(int u){
    int S=G[u].size();
    p[u]=1;
    For(i,0,S-1){
        int v=G[u][i].v;
        if (!p[v]&&d[v]==d[u]+G[u][i].w)
            add(u,v,G[u][i].w),add(v,u,G[u][i].w),gettree(v);
    }
}
void init(){
    read(n),read(m),read(k);k--;
    For(i,1,m){
        int u,v,w;
        read(u),read(v),read(w);
        G[u].pb(edge(v,w)),G[v].pb(edge(u,w));
    }
    For(i,1,n)sort(G[i].begin(),G[i].end());
    spfa(1);Set(p,0);
    gettree(1);Set(p,0);
}
void getcg(int rt,int sz,int &cg){
    int flag(1);
    siz[rt]=1;
    Rep(i,rt)
        if (!p[v]&&fa[rt]!=v){
            fa[v]=rt;
            getcg(v,sz,cg),siz[rt]+=siz[v];
            if (siz[v]>sz>>1)flag=0;
        }
    if(sz-siz[rt]>sz>>1)flag=0;
    if(flag)cg=rt;
}
int ans1,ans2;
void solve(int rt,int sz){
    int cg;
    fa[rt]=0;f[0][1]=1;
    if(sz>=k)getcg(rt,sz,cg);else return;
    siz[fa[cg]]=sz-siz[cg];fa[cg]=0;
    Rep(i,cg)
        if(!p[v]){
            while(!q.empty())q.pop();
            q.push(v),dep[v]=1,dis[v]=w[i],fa[v]=cg;
            while(!q.empty()){
                int u=q.front();q.pop();
                int K=dep[u];
                if(K>k)break;
                if (dis[u]>tmp[K][0])tmp[K][0]=dis[u],tmp[K][1]=0;
                if (dis[u]==tmp[K][0])tmp[K][1]+=1;
                Rep(j,u)
                    if(!p[v]&&fa[u]!=v) 
                        fa[v]=u,q.push(v),
                        dep[v]=K+1,dis[v]=dis[u]+w[j];
            }
            For(j,1,k){
                if(tmp[j][0]+f[k-j][0]>ans1)ans1=f[k-j][0]+tmp[j][0],ans2=0;
                if(tmp[j][0]+f[k-j][0]==ans1)ans2+=tmp[j][1]*f[k-j][1];
            }
            For(j,1,k){
                if(tmp[j][0]>f[j][0])f[j][0]=tmp[j][0],f[j][1]=0;
                if(tmp[j][0]==f[j][0])f[j][1]+=tmp[j][1];
                tmp[j][0]=tmp[j][1]=0;
            }   
        }
    p[cg]=1;
    For(i,0,k)f[i][0]=f[i][1]=0;
    Rep(i,cg)
        if(!p[v])
            solve(v,siz[v]);
}
int main(){
    init();
    solve(1,n);
    printf("%d %d\n",ans1,ans2);
    return 0;
}


發佈了32 篇原創文章 · 獲贊 0 · 訪問量 6938
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章