Jzoj4715 树上路径
2017-10-15 11:32
288 查看
给出一棵树,求出最小的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],统计答案即可
另一种方法是启发式合并,随便选一个根,将每个子树的节点加入数组中让后用归并排序合并,合并前维护一下统计答案
这样好像无论是菊花图还是一条链都是n^2的(雾),但是居然跑得很快
Code来自chengziqi学(ju)长(shen):
至于题解里面说的二分我没有看懂,可以参考其他神犇的博客
这是一道点分治的题目,不过做法多样,也可以用启发式合并水过去,好像还有一种方法是什么二分
如果直接点分治的话,我们将子树内所有的点排序,求出其所处的子树编号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 100010 using namespace std; struct Node{ int len,p; }s ; struct Edge{ int v,c,nt; } G[N<<1]; int d ,sz ,f ={1<<27},vis ,h ; int pnt ,n,rt,cnt=0,t,nS,nt ,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 1e16 struct 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; }
至于题解里面说的二分我没有看懂,可以参考其他神犇的博客
相关文章推荐
- 【jzoj4715】【树上路径】【树】【分治】【点分治】
- JZOJ 4715 树上路径 (点分)
- 【JZOJ 4715】 树上路径
- JZOJ 4715 【NOIP2016提高A组模拟8.19】树上路径
- 【JZOJ4715】【NOIP2016提高A组模拟8.19】树上路径
- 【JZOJ 4715】树上路径
- jzoj5290 【NOIP2017提高组A组模拟8.17】行程的交集 (树上路径交,dfs序+树状数组维护姿势)
- 【jzoj5055】【GDOI2017模拟二试4.12】【树上路径】【点分治】
- 【JZOJ5055】【GDOI2017模拟二试4.12】树上路径
- [JZOJ5055]树上路径
- [编程题]树上最长单色路径
- JZOJ5385. 【NOIP2017提高A组模拟9.23】Carry 树上倍增
- JZOJ 3767【BJOI2014】路径
- 蓝桥杯 历届试题 大臣的旅费 (两次dfs 之树上求最长路径)
- 【NOIP2016提高A组模拟8.19】树上路径
- 树上的路径 BZOJ 3784
- jzoj1520 破碎的路径
- NOIP2015 运输计划 树上差分(路径覆盖)
- Jzoj5234 外星人的路径
- Codeforces 739B(树上路径倍增及差分)