zoukankan      html  css  js  c++  java
  • BZOJ4016:[FJOI2014]最短路径树问题

    浅谈树分治: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
    ",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 edge_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
    ",ans1,ans2);
        }
    }
    
    int main() {
        Pre::init();
        edge_divide::fake();
        return 0;
    }
    
  • 相关阅读:
    CLBZDQ
    CF1559D 题解
    DP 的凸优化
    正睿暑期集训7B
    基于 TiSpark 的海量数据批量处理技术
    PowerDesigner16.5下载和安装教程
    使用TiDB MPP
    使用 TiDB 构建实时应用
    oracle转mysql数据库
    kafka-jdbc-connector-sink实现kafka中的数据同步到mysql
  • 原文地址:https://www.cnblogs.com/AKMer/p/10099029.html
Copyright © 2011-2022 走看看