GERALD07加强版:lct,主席树,边化点

Description:N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

传送门

lct这么神仙的东西一个题解都不写怎么行???

神仙思路啊。

其实不是很难但是的确不容易想到。

我们考虑答案是什么。

首先刚开始有n个点分别是联通块,然后你连了一些边使联通块减少了。

怎么减少的呢?就是区间的边的生成树上边的数量。因为如果不是生成树上的边,那么一定与生成树上的边成环了而不会合并联通块。

怎么判断边是不是区间内生成树上的边呢?判断依据就是它有没有和前面的边成环。

那么我们先把边连起来,当连边时我们发现这两个点已经联通时,这条边就可以取代出现的最早的那条边。

如果它取代的那条边不在区间之内,那么这条边就在生成树上。

所以就来一棵LCT,边化点后维护最大编号就行,把每条边插入之前询问会被替代的边,存在数组lst里。

那么对于每一组询问,问题就变成了问在数组lst下标[l,r]内lst值小于l的有几个。

用主席树维护一下就好了。

记住这种思路。

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 int c[400005][2],f[400005],w[400005],n,m,k,opt,fid[400005],lst[200005],q[400005];
 5 int x[200005],y[200005],ans,rt[200005],v[5000005],t[5000005][2],lz[400005],cnt;
 6 int find(int p){return fid[p]==p?p:fid[p]=find(fid[p]);}
 7 #define lc c[p][0]
 8 #define rc c[p][1]
 9 bool not_root(int p){return c[f[p]][0]==p||c[f[p]][1]==p;}
10 void rev(int p){lc^=rc^=lc^=rc;lz[p]^=1;}
11 void down(int p){if(lz[p])rev(lc),rev(rc),lz[p]=0;}
12 void up(int p){w[p]=min(p>n?p:1234567890,min(w[lc],w[rc]));}
13 void rotate(int p){
14     int fa=f[p],gr=f[fa],dir=c[fa][1]==p,br=c[p][!dir];
15     if(not_root(fa))c[gr][c[gr][1]==fa]=p; c[p][!dir]=fa; c[fa][dir]=br;
16     f[p]=gr; f[fa]=p; f[br]=fa; up(fa);
17 }
18 void splay(int p){
19     int res=p,top=0;q[++top]=p;
20     while(not_root(res))q[++top]=res=f[res];
21     while(top)down(q[top--]);
22     while(not_root(p)){
23         int fa=f[p],gr=f[fa];
24         if(not_root(fa))rotate(c[fa][1]==p^c[gr][1]==fa?fa:p);
25         rotate(p);
26     }
27     up(p);
28 }
29 void access(int p){for(int y=0;p;p=f[y=p])splay(p),rc=y,up(p);}
30 void make_root(int p){access(p);splay(p);rev(p);}
31 void split(int x,int y){make_root(x);access(y);splay(y);}
32 void cut(int x,int y){split(x,y);f[x]=c[y][0]=0;up(y);}
33 void link(int x,int y){make_root(x);f[x]=y;up(y);}
34 void build(int &p,int cpy,int adx,int l=0,int r=m){
35     if(!p)p=++cnt;
36     if(l==r){v[p]=v[cpy]+1;return;}
37     if(adx<=l+r>>1)build(t[p][0],t[cpy][0],adx,l,l+r>>1),t[p][1]=t[cpy][1];
38     else build(t[p][1],t[cpy][1],adx,(l+r>>1)+1,r),t[p][0]=t[cpy][0];
39     v[p]=v[t[p][0]]+v[t[p][1]];//printf("%d %d %d\n",l,r,v[p]);
40 }
41 int ask(int p1,int p2,int l,int r,int cl=0,int cr=m){//printf("%d %d %d %d\n",cl,cr,v[p2],v[p1]);
42     if(!(v[p2]-v[p1]))return 0;
43     if(l<=cl&&cr<=r)return v[p2]-v[p1];
44     return (l<=cl+cr>>1?ask(t[p1][0],t[p2][0],l,r,cl,cl+cr>>1):0)+(r>cl+cr>>1?ask(t[p1][1],t[p2][1],l,r,(cl+cr>>1)+1,cr):0);
45 }
46 int main(){w[0]=1234567890;
47     scanf("%d%d%d%d",&n,&m,&k,&opt);
48     for(int i=1;i<=n;++i)fid[i]=i;
49     for(int i=1;i<=m;++i){
50         scanf("%d%d",&x[i],&y[i]);
51         if(x[i]==y[i])lst[i]=i;
52         else if(find(x[i])!=find(y[i]))fid[fid[x[i]]]=fid[y[i]],link(x[i],n+i),link(n+i,y[i]);
53         else split(x[i],y[i]),lst[i]=w[y[i]]-n,cut(lst[i]+n,x[lst[i]]),cut(lst[i]+n,y[lst[i]]),
54             link(x[i],n+i),link(y[i],n+i);
55         build(rt[i],rt[i-1],lst[i]);//printf("%d\n",lst[i]);
56     }
57     for(int i=1,l,r;i<=k;++i){
58         scanf("%d%d",&l,&r);
59         if(opt)l^=ans,r^=ans;
60         ans=n-ask(rt[l-1],rt[r],0,l-1);
61         printf("%d\n",ans);
62     }
63 }

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11566956.html

时间: 2024-10-07 20:54:28

GERALD07加强版:lct,主席树,边化点的相关文章

[BZOJ3514]CodeChef MARCH14 GERALD07加强版(LCT+主席树)

3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2177  Solved: 834[Submit][Status][Discuss] Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. Input 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密.接下来M行,代表图中的每条边.接下来K行,每行两个整数L

BZOJ 3514: Codechef MARCH14 GERALD07加强版( LCT + 主席树 )

从左到右加边, 假如+的边e形成环, 那么记下这个环上最早加入的边_e, 当且仅当询问区间的左端点> _e加入的时间, e对答案有贡献(脑补一下). 然后一开始是N个连通块, 假如有x条边有贡献, 答案就是N-x. 用LCT维护加边, 可持久化线段树维护询问. O(NlogN) ------------------------------------------------------------------------------------ #include<cstdio> #inc

BZOJ_3514_Codechef MARCH14 GERALD07加强版_主席树+LCT

Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. Input 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密. 接下来M行,代表图中的每条边. 接下来K行,每行两个整数L.R代表一组询问.对于type=0的测试点,读入的L和R即为询问的L.R:对于type=1的测试点,每组询问的L.R应为L xor lastans和R xor lastans. Output K行每行一个整数代表该组询问的联通块个数. Sample

【LCT+主席树】BZOJ3514 Codechef MARCH14 GERALD07加强版

3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 2023  Solved: 778[Submit][Status][Discuss] Description N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. Input 第一行四个整数N.M.K.type,代表点数.边数.询问数以及询问是否加密. 接下来M行,代表图中的每条边. 接下来K行,每行两个整

[bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席树]

题面 bzoj上的强制在线版本 思路 首先可以确定,这类联通块相关的询问问题,都可以$LCT$+可持久化记录解决 用LCT维护生成树作为算法基础 具体而言,从前往后按照边的编号顺序扫一遍边 如果这条边两端不在同一个$LCT$联通块中,则$link$ 否则$cut$掉当前连接两条边的路径上的编号最小的边,并$link$ 记录$ntr[i]$表示第$i$条边触发第二种情况时$link$前$cut$掉的边的编号 如果触发第一种情况,则$ntr[i]=0$ 如果为自环,则$ntr[i]=i$ 这样记录之

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

BZOJ 3514 LCT+主席树

思路: //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=400500; int n,m,k,type,ch[N][2],fa[N],minn[N],rev[N],q[N],top,pre[N]; int root[N],tree[N*50],lson[N*50],rson[N*50],cnt,xx,yy,ans; struct Road{int x,y,wei;}road[N]; bool isr

BZOJ 3674 可持久化并查集加强版(主席树变形)

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 2515  Solved: 1107 [Submit][Status][Discuss] Description n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^4 Input Output Sample Inp

BZOJ3514 Codechef MARCH14 GERALD07加强版 LCT

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3514 题意概括 N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. N,M,Q<=200000 题解 http://hzwer.com/4358.html 这题hzwer还是写的很好的-- 代码 #include <cstring> #include <cstdio> #include <cstdlib> #include <al