[BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
来源:互联网 发布:雅各布矩阵和海森矩阵 编辑:程序博客网 时间:2024/05/16 07:23
题目描述
传送门
题解
首先,要求最大值最小,先想到二分。可以预处理出来所有路径所需要的时间。
关键是怎么check呢?
算法一
每次二分出mid了之后,将所有时间>=mid的路径上的边打标记(链剖+bit即可),然后枚举每一条边,如果这条边打的标记的数量=所有需要打标记的路径的数量,并且这条边是所有满足条件里的最大的,那么将这条边砍断。然后再判断这个最解是否可行就行了。
然而这个时间复杂度是
算法二
我们考虑在算法一的基础上进行优化。算法一的瓶颈主要是check的时候是
当然可以!方法就是——差分!这里用了一个非常常用的树上差分的思路:对于点x,y设r=lca(x,y)。在x+1,y+1,r-2,然后统计后缀和(x->f[x])。这样就可以把每一条路径都打上标记,然后查询就可以了。
虽然Po的代码在BZ上没快多少,但是在UOJ上可以发现算法二是明显更优的。
(比如愚蠢的ATP写了愚蠢的算法一结果被卡T辣!——ATP▽)
代码
#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 300005#define sz 19int n,m,x,y,z,Max,dfs_clock,ans;int tot,point[N],nxt[N*2],v[N*2],c[N*2];int h[N],dis[N],val[N],num[N],tmp[N],f[N][sz+5];struct hp{int x,y,lca,dis;}edge[N];void addedge(int x,int y,int z){ ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z; ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;}void build(int x,int fa){ num[++dfs_clock]=x; for (int i=1;i<sz;++i) { if ((h[x]-(1<<i))<1) break; f[x][i]=f[f[x][i-1]][i-1]; } for (int i=point[x];i;i=nxt[i]) if (v[i]!=fa) { f[v[i]][0]=x; h[v[i]]=h[x]+1;dis[v[i]]=dis[x]+c[i];val[v[i]]=c[i]; build(v[i],x); }}int lca(int x,int y){ if (h[x]<h[y]) swap(x,y); int k=h[x]-h[y]; for (int i=0;i<sz;++i) if ((1<<i)&k) x=f[x][i]; if (x==y) return x; for (int i=sz-1;i>=0;--i) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];}bool check(int mid){ int cnt=0,limit=0;memset(tmp,0,sizeof(tmp)); for (int i=1;i<=m;++i) if (edge[i].dis>mid) { ++tmp[edge[i].x];++tmp[edge[i].y];tmp[edge[i].lca]-=2; limit=max(limit,edge[i].dis-mid); cnt++; } if (!cnt) return true; for (int i=n;i>1;--i) tmp[f[num[i]][0]]+=tmp[num[i]]; for (int i=2;i<=n;++i) if (val[i]>=limit&&tmp[i]==cnt) return true; return false;}int find(){ int l=0,r=Max,mid,ans; while (l<=r) { mid=(l+r)>>1; if (check(mid)) ans=mid,r=mid-1; else l=mid+1; } return ans;}int main(){ scanf("%d%d",&n,&m); for (int i=1;i<n;++i) { scanf("%d%d%d",&x,&y,&z); addedge(x,y,z);Max+=z; } build(1,0); for (int i=1;i<=m;++i) { scanf("%d%d",&edge[i].x,&edge[i].y); edge[i].lca=lca(edge[i].x,edge[i].y); edge[i].dis=dis[edge[i].x]+dis[edge[i].y]-dis[edge[i].lca]*2; } ans=find(); printf("%d\n",ans);}
总结
树上差分的常见思路:
①利用dfs序的时间戳,一个点拆成两个点,每次在in+1,在out-1,然后bit统计前缀和,资瓷动态修改和查询子树访问次数。用于子树打标记。
②对于点x,y设r=lca(x,y)。在x+1,y+1,r-2,然后统计后缀和(x->f[x])。用于树链打标记,资瓷查询某条边的访问次数。
③对于点x,y设r=lca(x,y)。在x+1,y+1,r-1,father[r]-1,然后统计后缀和(x->f[x])。用于树链打标记,资瓷查询某个点的访问次数。
- [BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
- [BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
- [BZOJ4326][NOIP2015]运输计划(二分答案+树上差分)
- bzoj4326 & UOJ150【NOIP2015】运输计划 ( 树上差分 + lca )
- 【BZOJ4326】【二分答案】【树上差分】NOIP2015 D2T3 运输计划 题解
- [BZOJ4326][NOIP2015][倍增][二分][差分序列]运输计划
- 【NOIP2015】【bzoj4326】运输计划 LCA+差分+二分答案
- 【二分+LCA+差分】BZOJ4326(NOIP2015)[运输计划]题解
- BZOJ4326(NOIP2015)运输计划--二分+LCA+差分
- 洛谷 2680[NOIP2015] 运输计划 二分+lca+树上差分+dfs序
- [bzoj4326][NOIP2015] 运输计划 差分+LCA
- 【二分+LCA差分乱搞】BZOJ4326(UOJ150) NOIP2015 运输计划
- 【NOIP2015】运输计划 {二分答案+倍增+树上差分}
- 【BZOJ】4326 NOIP2015 运输计划 二分+LCA+树上差分
- BZOJ 4326: NOIP2015 运输计划 二分答案 树上差分
- NOIP2015 运输计划 二分答案+Tarjan LCA+树上差分
- LuoguP2680/UOJ150[NOIP2015] 运输计划 解题报告【二分答案+树上操作(LCA)+树上差分】
- bzoj4326: NOIP2015 运输计划(lca+二分)
- HTML/Javascript: 点击按钮页面刷新问题
- cmake安装
- 权势二进制
- BottomDialog 是一个通过 DialogFragment 实现的底部弹窗布局,并且支持弹出动画,支持任意布局http://shaohui.me
- windows 下安装elasticsearch 及其插件head,ik,marvel
- [BZOJ4326][NOIP2015]运输计划(二分+dfs序+树上差分)
- openwrt中使用ubus实现进程通信
- 路由器PandoraBox使用opkg安装ipk
- Linux下安装apache详解
- Android弹幕功能实现,模仿斗鱼直播的弹幕效果
- iOS icon 的设置与启动页的设置
- 网易秋招编程题——翻转数字
- WM_NCHITTEST消息的了解+代码实例进行演示
- Github 下载文件夹