Description
这道题lca可以拿60分.
枚举每条边变为黑洞,求最小的里程判断这条边是否在u到v的路径上
复杂度O(n*m)
#include<cstdio> #include<algorithm> using namespace std; const int N=1e5+5; struct X { int v,q,f,n; }x[N<<1]; int s=1,qd[N],zd[N],dep[N],fir[N],ol[N<<1][17],cnt,d[N],dfn[N]; bool vis[N]; void add(int u,int v,int q) { x[++s].n=x[u].f; x[x[u].f=s].v=v; x[s].q=q; } void dfs(int u) { vis[dfn[++s]=u]=1; ol[fir[u]=++cnt][0]=s; int t=s; for(int i=x[u].f;i;i=x[i].n) if(!vis[x[i].v]) { dep[x[i].v]=dep[u]+1; d[x[i].v]=d[u]+x[i].q; dfs(x[i].v); ol[++cnt][0]=t; } } int lca(int l,int r) { int k=0;l=fir[l];r=fir[r]; if(l>r) swap(l,r); for(;1<<(k+1)<=r-l+1;k++); return dfn[min(ol[l][k],ol[r-(1<<k)+1][k])]; } int main() { int n,m,ans=1e9; scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v,q; scanf("%d%d%d",&u,&v,&q); add(u,v,q);add(v,u,q); } for(int i=1;i<=m;i++) scanf("%d%d",&qd[i],&zd[i]); s=0;dfs(1); for(int j=1;1<<j<=cnt;j++) for(int i=1;i+(1<<j)-1<=cnt;i++) ol[i][j]=min(ol[i][j-1],ol[i+(1<<j-1)][j-1]); for(int i=1;i<n;i++) { int u=x[i<<1].v,v=x[i<<1|1].v,tans=0; if(dep[u]<dep[v]) swap(u,v); for(int j=1;j<=m;j++) { int t=lca(qd[j],zd[j]),dis=d[qd[j]]+d[zd[j]]-d[t]*2; if(dep[t]<=dep[v]&&max(dep[qd[j]],dep[zd[j]])>=dep[u]) { int lc1=lca(qd[j],u),lc2=lca(qd[j],v),lc3=lca(zd[j],u),lc4=lca(zd[j],v); if((lc1==u&&lc2==v)||(lc3==u&&lc4==v)) dis-=x[i<<1].q; } tans=max(dis,tans); } ans=min(ans,tans); } printf("%d",ans); return 0; }
80分对于链的情况进行特判。
对于链的情况进行二分。
check函数:对于所有大于x的路径在差分数组里标记。
如果差分数组中的数达到大于x的路径数且修改这条路可以使最大路径<mid即可以返回1
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+5; struct X { int v,q,f,n; }x[N<<1]; int s=1,qd[N],zd[N],dep[N],fir[N],ol[N<<1][17],cnt,d[N],dfn[N],n,m; bool vis[N]; void add(int u,int v,int q) { x[++s].n=x[u].f; x[x[u].f=s].v=v; x[s].q=q; } void dfs(int u) { vis[dfn[++s]=u]=1; ol[fir[u]=++cnt][0]=s; int t=s; for(int i=x[u].f;i;i=x[i].n) if(!vis[x[i].v]) { dep[x[i].v]=dep[u]+1; d[x[i].v]=d[u]+x[i].q; dfs(x[i].v); ol[++cnt][0]=t; } } int lca(int l,int r) { int k=0;l=fir[l];r=fir[r]; if(l>r) swap(l,r); for(;1<<(k+1)<=r-l+1;k++); return dfn[min(ol[l][k],ol[r-(1<<k)+1][k])]; } bool f(int a) { int mx=0,sl=0; memset(dep,0,sizeof(dep)); for(int i=1;i<=m;i++) if(d[zd[i]]-d[qd[i]]>a) ++dep[qd[i]],--dep[zd[i]],++sl,mx=max(mx,d[zd[i]]-d[qd[i]]); if(!mx) return 0; for(int i=1,lj=0;i<=n;i++) { lj+=dep[i]; if(lj==sl&&x[i<<1].q+a>=mx) return 0; } return 1; } int main() { int ans=1e9; bool pd=1; scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v,q; scanf("%d%d%d",&u,&v,&q); add(u,v,q);add(v,u,q); if((u==i&&v==i+1)||(v==i&&u==i+1)); else pd=0; } for(int i=1;i<=m;i++) scanf("%d%d",&qd[i],&zd[i]); if(pd) { int l=0,r=0; for(int i=2;i<=n;i++) d[i]=d[i-1]+x[(i-1)<<1].q; for(int i=1;i<=m;i++) { if(qd[i]>zd[i]) swap(qd[i],zd[i]); r=max(r,d[zd[i]]-d[qd[i]]); } while(l<r-1) { int mid=(l+r)>>1; if(f(mid)) l=mid; else r=mid; } printf("%d",r); return 0; } s=0;dfs(1); for(int j=1;1<<j<=cnt;j++) for(int i=1;i+(1<<j)-1<=cnt;i++) ol[i][j]=min(ol[i][j-1],ol[i+(1<<j-1)][j-1]); for(int i=1;i<n;i++) { int u=x[i<<1].v,v=x[i<<1|1].v,tans=0; if(dep[u]<dep[v]) swap(u,v); for(int j=1;j<=m;j++) { int t=lca(qd[j],zd[j]),dis=d[qd[j]]+d[zd[j]]-d[t]*2; if(dep[t]<=dep[v]&&max(dep[qd[j]],dep[zd[j]])>=dep[u]) { int lc1=lca(qd[j],u),lc2=lca(qd[j],v),lc3=lca(zd[j],u),lc4=lca(zd[j],v); if((lc1==u&&lc2==v)||(lc3==u&&lc4==v)) dis-=x[i<<1].q; } tans=max(dis,tans); } ans=min(ans,tans); } printf("%d",ans); return 0; }
100分:
同样进行二分,对于树上的点在d[u]++,d[v]++,d[lca(u,v)]-=2;所以i到fa[i]的路径覆盖数为sum(d[j])j为所有i的子节点。
ps:由于此题卡常数可能并不能在所有oj ac
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=3*1e5+5; struct X { int v,q,f,n; }x[N<<1],y[N<<1]; int s=1,r,cnt,d[N],fa[N],n,m,mx,lc[N<<1]; bool vis[N]; void add(int u,int v,int q,X *t) { t[++s].n=t[u].f; t[t[u].f=s].v=v; t[s].q=q; } int find(int a) { int b=a; while(fa[b]) b=fa[b]; while(a!=b) { int t=fa[a]; fa[a]=b; a=t; } return b; } void dfs(int u) { vis[u]=1; for(int i=x[u].f;i;i=x[i].n) if(!vis[x[i].v]) { d[x[i].v]=d[u]+x[i].q; dfs(x[i].v); fa[x[i].v]=u; } for(int i=y[u].f;i;i=y[i].n) if(vis[y[i].v]) { lc[i]=lc[i^1]=find(y[i].v); y[i].q=y[i^1].q=d[y[i].v]+d[u]-(d[lc[i]]<<1); r=max(r,y[i].q); } } bool dfs1(int u,int fa,int a) { for(int i=x[u].f;i;i=x[i].n) if(x[i].v!=fa) { if(!dfs1(x[i].v,u,a)) return 0; if(d[x[i].v]==cnt&&a+x[i].q>=mx) return 0; d[u]+=d[x[i].v]; } return 1; } bool f(int a) { mx=cnt=0; memset(d,0,sizeof(d)); for(int i=1;i<=m;++i) if(y[i<<1].q>a) { ++d[y[i<<1].v]; ++d[y[i<<1|1].v]; d[lc[i<<1]]-=2; ++cnt;mx=max(mx,y[i<<1].q); } if(!cnt) return 0; return dfs1(1,0,a); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;++i) { int u,v,q; scanf("%d%d%d",&u,&v,&q); add(u,v,q,x);add(v,u,q,x); } s=1;int l=0; for(int i=1;i<=m;++i) { int u,v; scanf("%d%d",&u,&v); add(u,v,0,y);add(v,u,0,y); } dfs(1); while(l<r-1) { int mid=(l+r)>>1; if(f(mid)) l=mid; else r=mid; } printf("%d",r); return 0; }