1. 程式人生 > >BZOJ4016:[FJOI2014]最短路徑樹問題

BZOJ4016:[FJOI2014]最短路徑樹問題

技術分享 push_back algo 最短路徑 += ostream %d 路徑 題目

淺談樹分治:https://www.cnblogs.com/AKMer/p/10014803.html

題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=4016

很討厭這種把兩個模板強行合在一起的出題人。。。

求出最短路之後按字典序去遞歸建樹就是字典序最小的最短路徑樹。

然後\(f[i][0]\)表示掃過的子樹裏,經過\(i\)個點可以得到的最長長度,\(f[i][1]\)表示方案數。\(g\)數組同理,不過存的是當前子樹的,然後維護一下隨便搞搞就\(A\)了。

點分治寫起來很順手,邊分治就賊有趣了喲。。。如果你繼續用上面那個表示意義,就會被下面這張圖卡掉(除非你冒著\(TLE\)

的風險不重新建樹):

技術分享圖片

\(2-1-3\)這條路徑會在點分做法下被忽視,所以\(f\)數組和\(g\)數組的第一個下標應該由表示點變成表示邊,這樣才不會被卡。可以保證,如果兩條原樹邊之間全是新加邊,那麽這兩條原樹邊在原樹上肯定是靠在一起的。

時間復雜度:\(O(nlogn)\)

空間復雜度:\(O(n)\)

點分治版代碼:

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;
#define fr first
#define sc second
 
const int maxn=3e4+5,maxm=6e4+5;
 
int n,m,limit;
int dis[maxn];
vector<pii>to[maxn];
 
int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}
 
namespace Pre {
    int tot;
    int now[maxn],son[maxm*2],pre[maxm*2],val[maxm*2];
     
    void add(int a,int b,int c) {
        pre[++tot]=now[a];
        now[a]=tot,son[tot]=b,val[tot]=c;
    }
 
    struct node {
        int u,dis;
 
        node() {}
 
        node(int _u,int _dis) {
            u=_u,dis=_dis;
        }
 
        bool operator<(const node &a)const {
            return dis<a.dis;
        }
    };
 
    struct Heap {
        int tot;
        node tree[maxm];
 
        void ins(node u) {
            tree[++tot]=u;
            int pos=tot;
            while(pos>1) {
                if(tree[pos]<tree[pos>>1])
                    swap(tree[pos],tree[pos>>1]),pos>>=1;
                else break;
            }
        }
 
        node pop() {
            node res=tree[1];
            tree[1]=tree[tot--];
            int pos=1,son=2;
            while(son<=tot) {
                if(son<tot&&tree[son|1]<tree[son])son|=1;
                if(tree[son]<tree[pos])
                    swap(tree[son],tree[pos]),pos=son,son=pos<<1;
                else break;
            }
            return res;
        }
    }T;
 
    void dijstra() {
        memset(dis,0x3f,sizeof(dis));
        dis[1]=0,T.ins(node(1,0));
        while(T.tot) {
            node tmp=T.pop();int u=tmp.u;
            if(dis[u]<tmp.dis)continue;
            for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
                if(dis[v]>dis[u]+val[p]) {
                    dis[v]=dis[u]+val[p];
                    T.ins(node(v,dis[v]));
                }
        }
    }
     
    void init() {
        n=read(),m=read(),limit=read();
        for(int i=1;i<=m;i++) {
            int a=read(),b=read(),c=read();
            add(a,b,c),add(b,a,c);
        }dijstra();
        for(int i=1;i<=n;i++) {
            for(int p=now[i],v=son[p];p;p=pre[p],v=son[p])
                if(dis[v]==dis[i]+val[p])to[i].push_back(make_pair(v,val[p]));
        }
    }
}
 
namespace point_divide {
    int siz[maxn];
    bool vis[maxn];
    int f[maxn][2],g[maxn][2];
    int tot,N,mx,rt,ans1,ans2;
    int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
 
    void add(int a,int b,int c) {
        pre[++tot]=now[a];
        now[a]=tot,son[tot]=b,val[tot]=c;
    }
 
    void build(int u) {
        vis[u]=1;if(to[u].empty())return;
        vector<pii>::iterator it;
        sort(to[u].begin(),to[u].end());
        for(it=to[u].begin();it!=to[u].end();it++) {
            pii tmp=*it;
            if(vis[tmp.fr])continue;
            add(u,tmp.fr,tmp.sc);
            add(tmp.fr,u,tmp.sc);
            build(tmp.fr);
        }
    }
 
    void find_rt(int fa,int u) {
        int res=0;siz[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v]&&v!=fa)find_rt(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
        res=max(res,N-siz[u]);
        if(res<mx)mx=res,rt=u;
    }
    void make_g(int fa,int u,int dep,int len) {
        if(dep<limit) {
            if(len>g[dep][0])g[dep][0]=len,g[dep][1]=0;
            if(len==g[dep][0])g[dep][1]++;
        }siz[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v]&&v!=fa)make_g(u,v,dep+1,len+val[p]),siz[u]+=siz[v];
    }
  
    void work(int u,int size) {
        N=size,mx=rt=n+1,find_rt(0,u);
        u=rt,vis[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v]){
                make_g(u,v,1,val[p]);
                for(int i=1;i<limit;i++) {
                    if(g[i][0]+f[limit-i-1][0]>ans1)ans1=g[i][0]+f[limit-i-1][0],ans2=0;
                    if(g[i][0]+f[limit-i-1][0]==ans1)ans2+=g[i][1]*f[limit-i-1][1];
                }
                for(int i=1;i<limit;i++) {
                    if(g[i][0]>f[i][0])f[i][0]=g[i][0],f[i][1]=0;
                    if(g[i][0]==f[i][0])f[i][1]+=g[i][1];
                    g[i][0]=g[i][1]=0;
                }
            }
        for(int i=1;i<=limit;i++)
            f[i][0]=f[i][1]=0;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[v])work(v,siz[v]);
    }
     
    void fake() {
        build(1);memset(vis,0,sizeof(vis));f[0][1]=1;
        work(1,n);printf("%d %d\n",ans1,ans2);
    }
}
 
int main() {
    Pre::init();
    point_divide::fake();
    return 0;
}

邊分治版代碼如下:

#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;
#define fr first
#define sc second

const int maxn=6e4+5,maxm=6e4+5;

int dis[maxn];
int cnt,n,m,limit;
vector<pii>to[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

namespace Pre {
    int tot;
    int now[maxn],son[maxm*2],pre[maxm*2],val[maxm*2];
    
    void add(int a,int b,int c) {
        pre[++tot]=now[a];
        now[a]=tot,son[tot]=b,val[tot]=c;
    }

    struct node {
        int u,dis;

        node() {}

        node(int _u,int _dis) {
            u=_u,dis=_dis;
        }

        bool operator<(const node &a)const {
            return dis<a.dis;
        }
    };

    struct Heap {
        int tot;
        node tree[maxm];

        void ins(node u) {
            tree[++tot]=u;
            int pos=tot;
            while(pos>1) {
                if(tree[pos]<tree[pos>>1])
                    swap(tree[pos],tree[pos>>1]),pos>>=1;
                else break;
            }
        }

        node pop() {
            node res=tree[1];
            tree[1]=tree[tot--];
            int pos=1,son=2;
            while(son<=tot) {
                if(son<tot&&tree[son|1]<tree[son])son|=1;
                if(tree[son]<tree[pos])
                    swap(tree[son],tree[pos]),pos=son,son=pos<<1;
                else break;
            }
            return res;
        }
    }T;

    void dijstra() {
        memset(dis,0x3f,sizeof(dis));
        dis[1]=0,T.ins(node(1,0));
        while(T.tot) {
            node tmp=T.pop();int u=tmp.u;
            if(dis[u]<tmp.dis)continue;
            for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
                if(dis[v]>dis[u]+val[p]) {
                    dis[v]=dis[u]+val[p];
                    T.ins(node(v,dis[v]));
                }
        }
    }
    
    void init() {
        cnt=n=read(),m=read(),limit=read()-1;
        for(int i=1;i<=m;i++) {
            int a=read(),b=read(),c=read();
            add(a,b,c),add(b,a,c);
        }dijstra();
        for(int i=1;i<=n;i++) {
            for(int p=now[i],v=son[p];p;p=pre[p],v=son[p])
                if(dis[v]==dis[i]+val[p])to[i].push_back(make_pair(v,val[p]));
        }
    }
}

namespace point_divide {
    int siz[maxn];
    bool vis[maxn];
    int f[maxn][2],g[maxn][2];
    int tot,N,mx,id,ans1,ans2;
    int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];

    void add(int a,int b,int c) {
        pre[++tot]=now[a];
        now[a]=tot,son[tot]=b,val[tot]=c;
    }

    void build(int u) {
        vis[u]=1;if(to[u].empty())return;
        vector<pii>::iterator it;
        sort(to[u].begin(),to[u].end());
        for(it=to[u].begin();it!=to[u].end();it++) {
            pii tmp=*it;
            if(vis[tmp.fr])continue;
            add(u,tmp.fr,tmp.sc);
            add(tmp.fr,u,tmp.sc);
            build(tmp.fr);
        }
        to[u].clear();
    }

    void find_son(int fa,int u) {
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(v!=fa)to[u].push_back(make_pair(v,val[p])),find_son(u,v);
    }

    void rebuild() {
        vector<pii>::iterator it;
        tot=1;memset(now,0,sizeof(now));
        for(int i=1;i<=cnt;i++) {
            int size=to[i].size();
            if(size<=2) {
                for(it=to[i].begin();it!=to[i].end();it++) {
                    pii tmp=*it;
                    add(i,tmp.fr,tmp.sc),add(tmp.fr,i,tmp.sc);
                }
            }
            else {
                pii u1=make_pair(++cnt,0),u2;
                if(size==3)u2=to[i].front();
                else u2=make_pair(++cnt,0);
                add(i,u1.fr,u1.sc),add(u1.fr,i,u1.sc);
                add(i,u2.fr,u2.sc),add(u2.fr,i,u2.sc);
                if(size==3) {
                    for(int j=1;j<=2;j++)
                        to[cnt].push_back(to[i].back()),to[i].pop_back();
                }
                else {
                    int p=0;
                    for(it=to[i].begin();it!=to[i].end();it++) {
                        if(!p)to[cnt-1].push_back(*it);
                        else to[cnt].push_back(*it);p^=1;
                    }
                }
            }
        }
    }

    void find_edge(int fa,int u) {
        siz[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[p>>1]&&v!=fa) {
                find_edge(u,v),siz[u]+=siz[v];
                if(abs(N-2*siz[v])<mx)
                    mx=abs(N-2*siz[v]),id=p>>1;
            }
    }

    void solve1(int fa,int u,int dep,int len,bool bo) {
        if(dep<limit&&bo) {
            if(len>f[dep][0])f[dep][0]=len,f[dep][1]=0;
            if(len==f[dep][0])f[dep][1]++;
        }siz[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[p>>1]&&v!=fa)
                solve1(u,v,dep+(val[p]>0),len+val[p],val[p]),siz[u]+=siz[v];
    }

    void solve2(int fa,int u,int dep,int len,bool bo) {
        if(dep<limit&&bo) {
            if(len>g[dep][0])g[dep][0]=len,g[dep][1]=0;
            if(len==g[dep][0])g[dep][1]++;
        }siz[u]=1;
        for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
            if(!vis[p>>1]&&v!=fa)
                solve2(u,v,dep+(val[p]>0),len+val[p],val[p]),siz[u]+=siz[v];
    }

    void work(int u,int size) {
        if(size<2)return;
        N=size,mx=id=cnt+1,find_edge(0,u);
        vis[id]=1;int u1=son[id<<1],u2=son[id<<1|1];
        solve1(0,u1,0,0,0),solve2(0,u2,0,0,0);
        for(int i=0;i<=limit-(val[id<<1]>0);i++) {
            if(g[i][0]+f[limit-i-(val[id<<1]>0)][0]+val[id<<1]>ans1)
                ans1=g[i][0]+f[limit-i-(val[id<<1]>0)][0]+val[id<<1],ans2=0;
            if(g[i][0]+f[limit-i-(val[id<<1]>0)][0]+val[id<<1]==ans1)
                ans2+=g[i][1]*f[limit-i-(val[id<<1]>0)][1];
        }
        for(int i=0;i<limit;i++)
            g[i][0]=f[i][0]=g[i][1]=f[i][1]=0;
        g[0][1]=f[0][1]=1;
        work(u1,siz[u1]),work(u2,siz[u2]);
    }
    
    void fake() {
        build(1);memset(vis,0,sizeof(vis));
        find_son(0,1),rebuild();f[0][1]=g[0][1]=1;
        work(1,cnt);printf("%d %d\n",ans1,ans2);
    }
}

int main() {
    Pre::init();
    point_divide::fake();
    return 0;
}

BZOJ4016:[FJOI2014]最短路徑樹問題