博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【bzoj2959】长跑【LCT+并查集】
阅读量:4546 次
发布时间:2019-06-08

本文共 4203 字,大约阅读时间需要 14 分钟。

长跑

Description

  某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。

  为了让同学们更好地监督自己,学校推行了刷卡机制。
  学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
  有以下三类事件:
  1、修建了一条连接A地点和B地点的跑道。
  2、A点的刷卡机台数变为了B。
  3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
  当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
  为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

Input

  输入的第一行包含两个正整数n,m,表示地点的个数和操作的个数。

  第二行包含n个非负整数,其中第i个数为第个地点最开始刷卡机的台数。
  接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。
  最初所有地点之间都没有跑道。
  每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。

Output

  输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。

码了一整个下午,终于过了样例,结果AC了……这题样例好强大。。

题解:

“为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。” 这句话是关键。因此,路途中经过的任意一个强联通分量的所有点权之和要全部加上。于是我们就动态维护强联通分量。开2个并查集,一个保存的是原树之间的关系用来判断连通性,一个保存的是强联通分量的代表节点。至于为什么要用第一个并查集,原因是…加速。于是每连一条边u-v,就用第一个并查集判断一下u,v是否连通。如果没有,就直接连接,否则意味着图中出现了一个环。于是,我们把这个环上的所有节点(也就是原来u-v的路径上的所有点)的点权移到一个代表节点上,然后丢掉其他所有点,还要把所有点的第二个并查集的父亲赋值为代表节点,因为它们属于的强联同分量改变了。怎么删除不要的点呢?只要保证不会在各种操作的时候跳到不要的节点即可。具体实现时,只要把所有的fa改为find(fa)即可,就会跳到代表节点上。剩下两个操作都是经典操作。不知道有没有什么更快的方法,但可以肯定的是,我的代码常数巨大==
代码:

#include
#include
using namespace std;const int N=150005;int n,m,op,u,v,fu0,fv0,fu1,fv1,pa[N][2],fa[N],ch[N][2],rev[N],val[N],sumv[N];int a[N],stk[N];int find(int u,int md){ return u==pa[u][md]?u:pa[u][md]=find(pa[u][md],md);}bool isroot(int u){ if(!fa[u]){ return true; } int tmp=find(fa[u],1); return u!=ch[tmp][0]&&u!=ch[tmp][1];}int which(int u){ return u==ch[find(fa[u],1)][1];}void pushup(int u){ if(!u){ return; } sumv[u]=val[u]+sumv[ch[u][0]]+sumv[ch[u][1]];}void reverse(int u){ rev[u]^=1; swap(ch[u][0],ch[u][1]);}void downtag(int u){ if(rev[u]){ if(ch[u][0]){ reverse(ch[u][0]); } if(ch[u][1]){ reverse(ch[u][1]); } rev[u]=0; }}void pushdown(int u){ stk[stk[0]=1]=u; for(;!isroot(u);u=fa[u]){ stk[++stk[0]]=fa[u]; } while(stk[0]){ downtag(stk[stk[0]--]); }}void rotate(int x){ int y=find(fa[x],1),z=find(fa[y],1),md=x==ch[y][1]; if(y==ch[z][0]||y==ch[z][1]){ ch[z][y==ch[z][1]]=x; } fa[x]=z; ch[y][md]=ch[x][!md]; if(ch[y][md]){ fa[ch[y][md]]=y; } ch[x][!md]=y; if(y){ fa[y]=x; } pushup(y); pushup(x);}void splay(int u){ pushdown(u); while(!isroot(u)){ if(!isroot(fa[u])){ rotate(which(fa[u])==which(u)?fa[u]:u); } rotate(u); }}void access(int u){ for(int v=0;u;v=u,u=find(fa[u],1)){ splay(u); ch[u][1]=v; pushup(u); }}void makeroot(int u){ access(u); splay(u); reverse(u);}int dfs(int rt,int u){ if(!u){ return 0; } pa[u][1]=rt; return dfs(rt,ch[u][0])+dfs(rt,ch[u][1])+val[u];}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ pa[i][0]=pa[i][1]=i; } for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sumv[i]=val[i]=a[i]; } for(int i=1;i<=m;i++){ scanf("%d%d%d",&op,&u,&v); if(op==1){ if((fu1=find(u,1))!=(fv1=find(v,1))){ if((fu0=find(u,0))!=(fv0=find(v,0))){ makeroot(fu1); fa[fu1]=fv1; pa[fv0][0]=fu0; }else{ makeroot(fu1); access(fv1); splay(fv1); val[fu1]=dfs(fu1,fv1); fa[fu1]=0; ch[fu1][0]=ch[fu1][1]=0; pushup(fu1); } } }else if(op==2){ splay(fu1=find(u,1)); val[fu1]-=a[u]; a[u]=v; val[fu1]+=a[u]; pushup(fu1); }else{ if(find(u,0)!=find(v,0)){ puts("-1"); continue; } fu1=find(u,1),fv1=find(v,1); makeroot(fu1); access(fv1); splay(fv1); printf("%d\n",sumv[fv1]); } } return 0;}

转载于:https://www.cnblogs.com/2016gdgzoi471/p/9476910.html

你可能感兴趣的文章
《大数据日知录》读书笔记-ch2数据复制与一致性
查看>>
个人冲刺01
查看>>
Ubuntu16.04源的问题
查看>>
mysql基础5(mysql命令集----表操作)
查看>>
DevExpress:下拉框绑定数据源 (ComboBoxEdit,LookUpEdit)
查看>>
视觉里程计06 Qt界面显示摄像头
查看>>
基于unity3d IFC的虚拟仿真系统
查看>>
BCS SET EMAIL
查看>>
linux 2.6 驱动笔记(一)
查看>>
SpringMVC与MyBatis整合方法
查看>>
获取当前系统运行目录
查看>>
多个tomcat实例运行的配置
查看>>
一种基于 Numpy 的 TF-IDF 实现报告
查看>>
wpf窗口阴影
查看>>
linux内核分析第四周-使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用...
查看>>
Centos 7升级内核
查看>>
Pandas 基本技巧
查看>>
hdu 1264
查看>>
hdu 1273不会的题
查看>>
(转)父子窗体的菜单合并及工具栏合并
查看>>