Jzoj4715 树上路径

来源:互联网 发布:阿里域名解析带端口号 编辑:程序博客网 时间:2024/04/27 22:20

给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)

这是一道点分治的题目,不过做法多样,也可以用启发式合并水过去,好像还有一种方法是什么二分

如果直接点分治的话,我们将子树内所有的点排序,求出其所处的子树编号belong,再求出next数组,next[i]=j表示belong[i]!=belong[j]的最小的j(i<j),这样的话,我们对于一个i就可以二分答案求出一个j使得v[i]+v[j]>=S的j,若belong[i]=belong[j]则令j=next[j],统计答案即可

#pragma GCC opitmize("O3")#pragma G++ opitmize("O3")#include<stdio.h>#include<string.h>#include<algorithm>#define N 100010using namespace std;struct Node{ int len,p; }s[N];struct Edge{ int v,c,nt; } G[N<<1];int d[N],sz[N],f[N]={1<<27},vis[N],h[N];int pnt[N],n,rt,cnt=0,t,nS,nt[N],S,E,A=1<<30;inline void gmin(int& x,int y){ if(x>y)x=y; }inline void gmax(int& x,int y){ if(x<y)x=y; }inline bool c1(Node a,Node b){ return a.len<b.len; }inline void adj(int x,int y,int c){ G[++cnt]=(Edge){y,c,h[x]}; h[x]=cnt; }void gSize(int x,int p){sz[x]=1;for(int v,i=h[x];i;i=G[i].nt)if(!vis[v=G[i].v] && v!=p){gSize(v,x);sz[x]+=sz[v];}}void gRoot(int x,int p){sz[x]=1; f[x]=0;for(int v,i=h[x];i;i=G[i].nt)if(!vis[v=G[i].v] && v!=p){gRoot(v,x);sz[x]+=sz[v];gmax(f[x],sz[v]);}gmax(f[x],nS-sz[x]);if(f[x]<f[rt]) rt=x;}void gParent(int x,int p){s[++t]=(Node){d[x],pnt[x]};for(int v,i=h[x];i;i=G[i].nt)if(!vis[v=G[i].v] && v!=p){if(p) pnt[v]=pnt[x];d[v]=d[x]+G[i].c;gParent(v,x);}}void gCal(int x){d[x]=0; t=0; pnt[x]=x;for(int i=h[x];i;i=G[i].nt) pnt[G[i].v]=G[i].v;gParent(x,0); sort(s+1,s+1+t,c1); nt[t]=t+1;for(int i=t-1;i;--i) nt[i]=(s[i].p!=s[i+1].p?i+1:nt[i+1]);for(int l=1,r=t;l<r;++l){for(;l<r&&s[r-1].len+s[l].len>=S;--r);if(s[l].p==s[r].p) r=nt[r];if(r<=t&&s[l].len+s[r].len>=S) gmin(A,s[l].len+s[r].len);}}void gDfs(int x){gCal(x); vis[x]=1; gSize(x,0);for(int v,i=h[x];i;i=G[i].nt)if(!vis[v=G[i].v]){rt=0; nS=sz[x];gRoot(v,x); gDfs(rt);}}int main(){scanf("%d%d%d",&n,&S,&E);for(int a,b,c,i=1;i<n;++i){scanf("%d%d%d",&a,&b,&c);adj(a,b,c); adj(b,a,c);}nS=n; gRoot(1,0); gDfs(rt);if(A>E) puts("-1"); else printf("%d\n",A);}
另一种方法是启发式合并,随便选一个根,将每个子树的节点加入数组中让后用归并排序合并,合并前维护一下统计答案

这样好像无论是菊花图还是一条链都是n^2的(雾),但是居然跑得很快

Code来自chengziqi学(ju)长(shen):

#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long LL;const LL maxn=2e5+10;#define INF 1e16struct edge {LL t,w,next;}e[2*maxn];LL last[maxn],size[maxn],deep[maxn],f[maxn],g[maxn];LL n,m,num,cnt,total,root,left,sum,right,h[maxn];bool vis[maxn];LL read(){LL 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 write(LL x){if (x<0) putchar('-'),x=-x;if (x>9) write(x/10);putchar(x%10+'0');}void road(LL x,LL y,LL z) {e[++num].t=y; e[num].w=z; e[num].next=last[x]; last[x]=num;}void get_root(LL x,LL fa){size[x]=1; f[x]=0;for (int i=last[x];i;i=e[i].next){LL y=e[i].t;if (vis[y]==true || y==fa)continue;get_root(y,x);size[x]+=size[y];f[x]=max(f[x],size[y]);}f[x]=max(f[x],total-size[x]);if (f[x]<f[root]) root=x; }void merge_sort(LL l,LL r,LL mid){LL s=l,t=mid+1,k=l-1;for (int i=l;i<=r;i++) h[i]=g[i];while (s<=mid && t<=r)if (h[s]<h[t]) g[++k]=h[s++];else g[++k]=h[t++];while (s<=mid) g[++k]=h[s++];while (t<=r) g[++k]=h[t++];}void work(LL x,LL fa){g[++cnt]=0; LL k=cnt+1,t=0;for (int i=last[x];i;i=e[i].next){LL y=e[i].t;if (y==fa) continue;t=cnt+1; work(y,x);for (int j=t;j<=cnt;j++){g[j]+=e[i].w;if (g[j]>=left && g[j]<=right)sum=min(sum,g[j]);if (g[j]>right){cnt=j-1; break;}}LL s=cnt;if (s<t) continue;for (int j=k;j<t;j++){while (g[j]+g[s]>=left && s>t) s--;if (g[j]+g[s]<left && s<cnt) s++;if (g[j]+g[s]>=left)sum=min(sum,g[j]+g[s]);if (s==t && h[s]+h[j]>=left) break;}if (k<t) merge_sort(k,cnt,t-1);}}int main(){root=sum=num=cnt=0; f[0]=INF;memset(vis,false,sizeof(vis));n=read(); left=read(); right=read();for (int i=1;i<n;i++){LL x=read(),y=read(),z=read();road(x,y,z); road(y,x,z);}total=n; get_root(1,0);sum=INF; work(root,0);if (sum==INF) write(-1);else write(sum);putchar('\n'); return 0;}
至于题解里面说的二分我没有看懂,可以参考其他神犇的博客

原创粉丝点击