沉迷Link-Cut tree无法自拔之:[BZOJ2594][Wc2006]水管局长数据加强版

来自蒟蒻 \(Hero \_of \_Someone\) 的 \(LCT\) 学习笔记
$
$
这应该算是道套路题吧, 如果将图中的边转换成点, 再将边权变点权, 就可以用 \(LCT\) 来维护了
这道题的基本做法就是, 用 \(LCT\) 来动态地维护最小生成树,
如果这样做的话, 题目中要求的删边操作就不太好搞,
但是既然只有删边操作的话, 我们就可以考虑离线处理,
将不会被删除的边先加进图中跑 \(Kruskal\) , 然后化删边为添边, 倒着来处理每一组询问.
$
$
此外, 用 \(LCT\) 维护 \(MST\) , 就是在添边的时候如果遇到环且环上最长的边边权大于当前边, 就将最大边 \(cut\) , 再将当前边添入

//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define N (100010)
#define M (1000010)
#define RG register
using namespace std;
inline int gi(){ RG int x=0; RG char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar();
  while('0'<=ch&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x; }

int n,m,q;
struct Edge{
  int u,v,w;
  bool operator<(const Edge&a)const{
    return u==a.u?v<a.v:u<a.u;
  }
  bool operator<(const pair<int,int>& a)const{
    return u==a.first?v<a.second:u<a.first;
  }
}e[M];

struct Que{ int op,x,y; }Q[N];

//-----------------------------------------------------------
int Fa[N];
inline int Find(RG int x){ return Fa[x]==x?x:Fa[x]=Find(Fa[x]); }

struct EEE{
  int u,v,w,id;
  bool operator<(const EEE& a)const{ return w<a.w; }
  void operator=(const Edge& a){
    u=a.u,v=a.v,w=a.w,id=(&a)-e;
  }
}E[M];

int num,head[N],nxt[N<<1],to[N<<1],w[N<<1]; //w为该边原来的编号
inline void add(int u,int v,int d){
  nxt[++num]=head[u];to[num]=v;w[num]=d;head[u]=num;
  nxt[++num]=head[v];to[num]=u;w[num]=d;head[v]=num;
}

bool v[M];
inline void kruskal(){
  RG int top=0;
  for(RG int i=1;i<=n;i++) Fa[i]=i;
  for(RG int i=1;i<=m;i++){
    if(v[i]) continue;
    E[++top]=e[i];
  }
  sort(E+1,E+top+1);
  for(RG int i=1;i<=top;i++){
    RG int x=E[i].u,y=E[i].v;
    RG int fx=Find(x),fy=Find(y);
    if(fx==fy) continue;
    Fa[fx]=fy; add(x,y,E[i].id);
  }
}

inline void init(){
  n=gi(),m=gi(),q=gi();
  for(RG int i=1;i<=m;i++){
    e[i].u=gi(),e[i].v=gi(),e[i].w=gi();
  }
  sort(e+1,e+m+1);
  for(RG int i=1;i<=q;i++){
    Q[i].op=gi(),Q[i].x=gi(),Q[i].y=gi();
    if(Q[i].op==2){
      RG int V=lower_bound(e+1,e+m+1,make_pair(Q[i].x,Q[i].y))-e;
      Q[i].x=V; v[V]=1;
    }
  }q++;
  kruskal();
}
//---------------------------------------------------------------

int val[N+M],Max[N+M];
int ch[N+M][2],fa[N+M],rev[N+M];

inline void cur(int x,int y){ val[x]=Max[x]=y; }

int vis[N];
inline void dfs(RG int x,RG int f,RG int d){
  if(vis[x]) return ;
  vis[x]=1; cur(x,0);
  if(f){ cur(n+d,d); fa[n+d]=f; fa[x]=n+d; } //将边转换成点插入树中
  for(RG int i=head[x];i;i=nxt[i])
    dfs(to[i],x,w[i]);
}

inline bool cnm(int x,int y){ return e[x].w<e[y].w; }
inline void up(int x){
  Max[x]=max(Max[ch[x][0]],Max[ch[x][1]],cnm);
  Max[x]=max(Max[x],val[x],cnm);
}

inline void reverse(int x){
  swap(ch[x][0],ch[x][1]);
  rev[x]^=1;
}

inline void down(int x){
  if(!rev[x]) return ;
  reverse(ch[x][0]);
  reverse(ch[x][1]);
  rev[x]=0;
}

inline bool is_root(int x){ return ch[fa[x]][0]!=x && x!=ch[fa[x]][1]; }

inline bool lr(int x){ return x==ch[fa[x]][1]; }

inline void rotate(int x){
  RG int y=fa[x],z=fa[y],k=lr(x);
  if(!is_root(y)) ch[z][lr(y)]=x;
  fa[x]=z; fa[ch[x][k^1]]=y; fa[y]=x;
  ch[y][k]=ch[x][k^1]; ch[x][k^1]=y;
  up(y); up(x);
}

int st[N+M];
inline void splay(int x){
  RG int y=x,top=0;
  while(1){
    st[++top]=y;
    if(is_root(y)) break;
    y=fa[y];
  }
  for(RG int i=top;i;i--) down(st[i]);
  while(!is_root(x)){
    if(!is_root(fa[x])) rotate(lr(x)^lr(fa[x])?x:fa[x]);
    rotate(x);
  }
}

inline void access(int x){
  RG int y=0;
  while(x){ splay(x);
    ch[x][1]=y; fa[y]=x;
    up(x); y=x; x=fa[x];
  }
}

inline void make_root(int x){
  access(x); splay(x); reverse(x);
}

inline int query(int x,int y){
  make_root(x); access(y); splay(y);
  return Max[y];
}

inline int find(int x){
  while(fa[x]) x=fa[x];
  return x;
}

inline void link(int x,int y){
  if(find(x)==find(y)) return ;
  make_root(x); fa[x]=y;
}

inline void cut(int x,int y){
  make_root(x); access(y); splay(y);
  if(ch[y][0]==x) ch[y][0]=0,fa[x]=0,up(y);
}

inline void Insert(int id){
  RG int x=e[id].u,y=e[id].v;
  if(find(x)==find(y)){
    RG int tmp=query(x,y);
    if(e[tmp].w<=e[id].w) return ;
    cut(n+tmp,e[tmp].u);
    cut(n+tmp,e[tmp].v);
  }
  cur(n+id,id);
  link(x,n+id);
  link(y,n+id);
}

int Ans[N],top;
inline void work(){
  for(RG int i=1;i<=n;i++) dfs(i,0,0);
  while(--q){
    if(Q[q].op==2) Insert(Q[q].x);
    else Ans[++top]=e[query(Q[q].x,Q[q].y)].w;
  }
  for(RG int i=top;i;i--) printf("%d\n",Ans[i]);
}

int main(){ init(); work(); return 0; }
时间: 2024-08-12 21:20:13

沉迷Link-Cut tree无法自拔之:[BZOJ2594][Wc2006]水管局长数据加强版的相关文章

[bzoj2594][Wc2006]水管局长数据加强版

论蒟蒻的自我修养T_T.. 和noi2014魔法森林基本一样...然而数据范围大得sxbk...100w你告诉我(n+m)log(n+m)可过?[掀桌] 蒟蒻又蠢了..复杂度应该是O((n+q)log(n+m))吧.. 一开始数组开太小re了两发(要开到maxn+maxm),然后又开太大mle一发,然后无限tle...把记录类型全改成数组还是tle.... 最后把非lct部分改得和黄学长全部一样终于20+s卡过去了......... 然后发现自己原来是有个地方写萎了..一开始把没被删的边做kru

[BZOJ2594] [Wc2006]水管局长数据加强版(LCT + kruskal + 离线)

传送门 WC这个题真是丧心病狂啊,就是想学习一下怎么处理边权,给我来了这么一个破题! ORZ hzwer 临摹黄学长代码233 但还是复杂的一匹 理一下思路吧 题目大意:给定一个无向图,多次删除图中的某一条边,求两点间路径最大值的最小值 求两点间的路径最大值的最小值的话,可以求最小生成树,那么这个值就是最小生成树上两点间路径上的最大值 但是题目要求是删除边,LCT维护最小生成树不支持删边操作,那么就离线处理,倒着加边,用LCT维护. 就是这个离线处理是最恶心的. 来说说如何处理边权,把边也抽象成

【bzoj2594】[Wc2006]水管局长数据加强版

真是神题 当时调了几天没调出来 后来没管了 当时把fread去掉就TLE,加上就RE 一直在底下跟网上的程序拍,尝试各种优化常数都没用 拍出几组不一样的,发现我是对的,醉了,网上那个是怎么过的 记一下这蛋疼的代码 1 #include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cstdio> 6 #include<ma

BZOJ2594水管局长数据加强版

Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了.嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项. 在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗.消毒等等.嘟嘟在

bzoj-2594 水管局长数据加强版

题意: 给出一个n个点m条边的无向图,边上有权值: Q次询问,每次有两种操作: 1.求x,y两点路径上的最大值的最小值: 2.删除一条边: 保证删除的边存在,保证图时刻连通,保证不会出现重边和自环: n≤100000,m≤1000000,Q≤100000: 题解: 这是一个动态图问题,但是由于询问操作的特殊性,我们也可以转化到树上做: 仔细看看不就是带删边的货车运输吗! 那么用LCT来维护最小生成树查询操作1就可以了: 但是LCT维护最小生成树是不能删边的: 考虑倒着处理询问,删边就成了加边:

bzoj2594水管局长数据加强版题解(未完待续)

题目大意 给一张带权无向图,无重边和自环,有如下操作: 删除某条边,保证这条边在删除前一定存在,并且不破坏原图连通性: 询问两点之间所有路径中最小权值的最大值是多少: 题解 问题的答案显然在原图的最小生成树上,于是本题就变成了动态维护删边最小生成树. 然而LinkCutTree维护最小生成树时并不支持删边操作,所以要离线处理,先删掉该删掉的边,再求最小生成树,把所有操作倒过来用LCT维护. 如何用LCT维护动态添边最小生成树 设原图中有n个点,m条边: 把每条边视为一个点,编号为n+1,n+2,

Codeforces Round #339 (Div. 2) A. Link/Cut Tree

A. Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, which is based on Splay trees. Specifically, he is now studying the expose procedure. Unfortunately, Rostislav is unable to understand the definition

AC日记——【模板】Link Cut Tree 洛谷 P3690

[模板]Link Cut Tree 思路: LCT模板: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 300005 int n,m,val[maxn]; int top,ch[maxn][2],f[maxn],xr[maxn],q[maxn],rev[maxn]; inline void in(int &now) { int if_z=1;now=0; char Cget=getchar(); while

bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isroot(int x):判断x是否为所在重链(splay)的根 void down(int x):下放各种标记 void rotate(int x):在x所在重链(splay)中将x旋转到fa[x]的位置上 void splay(int x):在x坐在重链(splay)中将x旋转到根 void acce