BZOJ4016:[FJOI2014]最短路徑樹問題
阿新 • • 發佈:2018-12-11
技術分享 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]最短路徑樹問題