【uoj150】 NOIP2015—运输计划
http://uoj.ac/problem/150 (题目链接)
题意
给出一棵树以及m个询问,可以将树上一条边的权值修改为0,求经过这样的修改之后最长的边最短是多少。
Solution
老早就听说过这道题了,好像使用树链剖分。
先树链剖分求出每个询问的路程,最长的最短,可以用二分做。二分最长的边的大小,也就是最后的答案,问题来了,怎么判断这个答案是否可行呢?
我们记录下所有超出当前答案的询问的个数p,用d记录下符合条件的边比当前二分的答案最大大多少,并给所有询问的两端点u,v的sum[]加上1,给他们的最近公共祖先f的sum[]减去2。这样做有什么用呢?这样就可以统计每条边经过了多少次了。
我们通过dfs,每经过一条边i,就把cnts[i]加上当前节点的sum值,这就代表有多少个点会经过这条边,回溯的时候更新sum即可。
最后的时候如果存在一条边被经过的次数正好等于当前询问数p,并且这条边的长度大于等于d,那么就是合法的,否则不合法。
其实这样的话根本就不用写树链剖分,dfs一遍就可以记录两点间距离了。。。然而树链剖分版不知道为什么最后uoj上extra test被卡的爆空间了。。好像是爆栈,于是手动开无限栈,MLE?!而且读入优化也gi了,真的鬼畜。。。无奈最后换成dfs版,没想到TLE。。。为什么bzoj上就能AC捏。
细节
差分的时候计数器数组cnts开成边数的空间。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | // uoj150 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define pragma comment(linker,"/STACK:1024000000,1024000000") #define LL long long #define inf 2147483640 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; int getint() { int f=1,x=0; 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; } const int maxn=300010; struct edge { int w,to,next;}e[maxn<<1]; struct ask { int u,v,dis;}q[maxn]; int bin[30],fa[maxn][30],head[maxn],deep[maxn],sum[maxn],d[maxn],cnts[maxn<<1]; int n,m,cnt,num; void link( int u, int v, int w) { e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w; e[++cnt].to=u;e[cnt].next=head[v];head[v]=cnt;e[cnt].w=w; } void dfs1( int x) { for ( int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for ( int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) { d[e[i].to]=d[x]+e[i].w; deep[e[i].to]=deep[x]+1; fa[e[i].to][0]=x; dfs1(e[i].to); } } int lca( int x, int y) { if (deep[x]<deep[y]) swap(x,y); int t=deep[x]-deep[y]; for ( int i=0;bin[i]<=t;i++) if (t&bin[i]) x=fa[x][i]; for ( int i=20;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return x==y?x:fa[x][0]; } void dfs( int x) { for ( int i=head[x];i;i=e[i].next) if (e[i].to!=fa[x][0]) { dfs(e[i].to); sum[x]+=sum[e[i].to]; cnts[i]=sum[e[i].to]; } } bool check( int mid) { int d=0,p=0; memset (sum,0, sizeof (sum)); for ( int u=q[1].u,v=q[1].v,i=1;i<=n;i++,u=q[i].u,v=q[i].v) if (q[i].dis>mid) { sum[u]++;sum[v]++; sum[lca(u,v)]-=2; p++; d=max(d,q[i].dis-mid); } dfs(1); for ( int i=1;i<=cnt;i++) if (p==cnts[i] && e[i].w>=d) return 1; return 0; } int main() { bin[0]=1; for ( int i=1;i<=20;i++) bin[i]=bin[i-1]<<1; scanf ( "%d%d" ,&n,&m); for ( int u,v,w,i=1;i<n;i++) { scanf ( "%d%d%d" ,&u,&v,&w); link(u,v,w); } dfs1(1); int L=0,R=-inf,ans=0; for ( int u,v,i=1;i<=m;i++) { q[i].u=u=getint(),q[i].v=v=getint(); int f=lca(u,v); q[i].dis=d[u]+d[v]-2*d[f]; R=max(q[i].dis,R); } while (L<=R) { int mid=(L+R)>>1; if (check(mid)) {ans=mid;R=mid-1;} else L=mid+1; } printf ( "%d" ,ans); return 0; } |
This passage is made by MashiroSky.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验