首页 > 其他分享 >luogu P3733 [HAOI2017] 八纵八横 题解【线段树分治+线性基+可撤销并查集+bitset】

luogu P3733 [HAOI2017] 八纵八横 题解【线段树分治+线性基+可撤销并查集+bitset】

时间:2023-07-29 16:36:13浏览次数:48  
标签:int 题解 findd 查集 topp MAXN bitset luogu void

目录

题目大意

题目链接

给出一张 \(n\) 个点 \(m\) 条边的连通无向图,边带边权 \(w_i\) 。有以下三种操作,共 \(q\) 次:
\(\centerdot\)在点 \(x,y\) 之间加入一条边权为\(w_i\)的边,如果这是第\(i\)个此种操作,则记这条新边为第\(i\)条。
\(\centerdot\)将第\(k\)条新边权值改为\(w_i\) 。
\(\centerdot\)删掉第\(k\)条新边,保证这条边之后不会再出现。
对于初始状态和每次操作后,从图中找到一条从\(1\)出发,并回到\(1\) 的一条权值最大路径,路径权值定义为经过边边权的异或和。点边均可多次经过,边经过多次边权也会被计算多次。
(\(1\leqslant n,m \leqslant 500,1\leqslant q,\log_2w_i\leqslant 10^3\),加边操作不超过 \(550\)次)

解题思路

首先发现可以任意获取一个环。也就是说,获取一个环的代价为零。
那么我们可以考虑以下几个步骤:

  1. 找到所有的环。
  2. 计算对于每个环取或不取的最大值。
  3. 修改环的存在。

首先感性理解:如果一个环的所有边中只有一条非树边,那么我们称其为简单环;针对所有的非简单环,我们都可以利用几个简单环拼成非简单环。
那么我们只需要考虑所有的简单环即可。而这个操作可以使用可撤销并查集线性基实现。具体题目见P4151 [WC2011] 最大XOR和路径
那么本题的关键点就是如何修改线性基的环。
考虑到既有添加操作,又有删除操作,不妨使用离线算法线段树分治。
记得进行并查集撤销的时候需要清除干净。
同时进行异或操作的时候可以使用std::bitset 优化时间复杂度。

code

#include<iostream>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
using namespace std ;

const int MAXN = 2e3+10 ;
struct Edge{
	int from ,to ;
	bitset<MAXN> w ;
}edge[MAXN] ;
char opt[10] ;
int stack[MAXN] ,top ;

inline void get_bit(bitset<MAXN> &x){
	char str[MAXN] ;
	x.reset() ;
	scanf("%s" , str) ;
	int len = strlen(str) ;
	for(int i = 0;i < len;++i)	if(str[i] == '1')	x.set(len - i - 1) ;	
}

inline void print(const bitset<MAXN> &x){
	int flag = 0; if (x.none()) return putchar('0'), void();
    for (int i = MAXN - 1; ~i; --i)
        if (flag && x[i] == 0) putchar('0');
        else if (x[i] == 1) putchar('1'), flag = 1;
}

struct Linear_basis{
	bitset<MAXN> c[MAXN] ;
	int lbsta[MAXN] ,lbtp ;
	inline void insert(bitset<MAXN> x){
		for(int i = MAXN-1;i >= 0;--i){
			if(x[i]){
				if(c[i].none()){
					lbsta[++lbtp] = i ;
					c[i] = x ;
					return ;
				}else{
					x ^= c[i] ;
				}
			}
		}
	}
	
	inline void del(int pos){
		while(lbtp != pos){
			c[lbsta[lbtp]].reset() ;
			lbtp-- ;
		}
	}
	
	inline bitset<MAXN> query(){
		bitset<MAXN> ret ;
		for(int i = MAXN-1;i >= 0;--i){
			if(ret[i] == 0){//不存在当前值,说明可以更大 
				if(c[i].any())	ret ^= c[i] ;
			}
		}
		return ret ;
	}
}lb ;

struct DSU{
	int fa[MAXN] ,siz[MAXN] ;
	bitset<MAXN> dis[MAXN] ;
	struct mem{
		int x ,y ,s ;
	}sta[MAXN] ;
	int topp ;
	
	inline void init(int n){
		for(int i = 1;i <= n;++i)	fa[i] = i ,siz[i] = 1 ; 
	}
	
	inline int findd(int x){
		return x == fa[x] ? x : findd(fa[x]) ;
	}
	
	inline bitset<MAXN> finddis(int x){
		return x == fa[x] ? dis[x] : dis[x] ^ finddis(fa[x]) ; 
	}
	
	inline void merge(Edge x){
		int u = x.from ,v = x.to ;
		bitset<MAXN> w = x.w ;
		if(findd(u) == findd(v)){
			lb.insert(finddis(u) ^ finddis(v) ^ w) ;
			return  ;
		}
		if(siz[findd(u)] > siz[findd(v)])	swap(u , v) ;
		sta[++topp] = mem{findd(u) , findd(v) , siz[findd(v)]} ;
		siz[findd(v)] += siz[findd(u)] ;
		dis[findd(u)] = finddis(u) ^ finddis(v) ^ w ;
		fa[findd(u)] = findd(v) ;
	}
	
	inline void del(int pos){
		while(topp != pos){
			fa[sta[topp].x] = sta[topp].x ;
			siz[sta[topp].y] -= siz[sta[topp].x] ;
			dis[sta[topp].x] = 0 ;
			topp-- ;
		}
	}
}dsu ;

struct Segtree{
	struct Node{
		vector<Edge>	e ;
		int l ,r ;
	}tree[MAXN << 2] ;
	
	inline void build(int node , int l , int r){
		tree[node].l = l ,tree[node].r = r ;
		if(l == r)	return ;
		int mid = (l + r) >> 1 ;
		build(node << 1 , l , mid) ;
		build(node << 1 | 1 , mid+1 , r) ;
	}
	
	inline void update(int node , int l , int r , Edge v){
		if(l <= tree[node].l && tree[node].r <= r){
			tree[node].e.push_back(v) ;
			return ;
		}
		int mid = (tree[node].l + tree[node].r) >> 1 ;
		if(l <= mid)	update(node << 1 , l , r , v) ;
		if(mid < r)	update(node<<1|1 , l , r , v) ;
	}
	
	inline void query(int node){
		int mem1 = lb.lbtp ;
		int mem2 = dsu.topp ;
		for(auto v: tree[node].e)	dsu.merge(v) ;
		if(tree[node].l == tree[node].r)	print(lb.query()) ,printf("\n") ;
		else query(node << 1) ,query(node << 1 | 1) ;
		lb.del(mem1) ,dsu.del(mem2) ;
	}
}seg ;

int main(){
	int n ,m ,p ;
	scanf("%d%d%d" , &n , &m , &p) ;
	bitset<MAXN> w ;
	seg.build(1 , 0 , p) ;
	dsu.init(n) ;
	for(int i = 1;i <= m;++i){
		int u ,v ;
		scanf("%d%d" , &u , &v) ;
		get_bit(w) ;
		seg.update(1 , 0 , p , Edge{u , v , w}) ;
	}
	for(int i = 1;i <= p;++i){
		scanf("%s" , opt) ;
		if(opt[0] == 'A'){
			int u ,v ;
			scanf("%d%d" , &u , &v) ;
			get_bit(w) ;
			edge[++top] = Edge{u , v , w} ;
			stack[top] = i ;
		}else{
			int k ;
			if(opt[1] == 'a'){
				scanf("%d" , &k) ;
				seg.update(1 , stack[k] , i-1 , edge[k]) ;
				stack[k] = 0 ;
			}else{
				scanf("%d" , &k) ;
				get_bit(w) ;
				seg.update(1 , stack[k] , i-1 , edge[k]) ;
				stack[k] = i ;
				edge[k].w = w ;
			}
		}
	}
	for(int i = 1;i <= top;++i)	if(stack[i])	seg.update(1 , stack[i] , p , edge[i]) ;
	seg.query(1) ; 
	return 0 ;
}

标签:int,题解,findd,查集,topp,MAXN,bitset,luogu,void
From: https://www.cnblogs.com/adolf-stalin/p/17590016.html

相关文章

  • P9459 浴眼盯真 题解
    由于我不会使用正则表达式,所以我只能使用基础Python语法QwQ。[input().split()for_inrange(int(input()))]是个列表生成器,效果是产生一个长度为\(T\)的列表,列表的元素是以每一行以空格为分割符的字符串列表。for(a,b,c,d)in[]可以用\(a,b,c,d\)来复制列表中每个元素......
  • P9451 [ZSHOI-R1] 新概念报数 题解
    满足\(\operatorname{popcount}(x)<3\)的数实际上很少,直接把所有这些数扔到set里面,询问就返回set中\(x\)的下一个元素即可。记得开longlong。set内的元素数量是\(\log^2w\),所以复杂度是\(\mathcalO(\log^2w\log\log^2w+T\log\log^2w)=\mathcalO(\log^2w\log\logw......
  • 【题解】Luogu[P4711] 「化学」相对分子质量
    Link一道简单的模拟题,评绿可能有点高了。因为没有括号嵌套,难度瞬间降了一个档次,我们直接对着化学式扫一遍即可。若扫到左括号,说明接下来都是在括号内的,我们标记一下。若扫到大写字母,说明出现了一个新的元素,那么我们就看后面是否有下标,若有则类似于快读的方式直接处理后面的数......
  • [USACO13DEC] The Bessie Shuffle S 洗牌 题解
    提供一种思路,可以做到\(O(n)\)。目前是全OJ最优解,跑到了79ms。update2023.07.29完工,期望无bug(暑假快乐吖o( ̄▽ ̄)ブ)update2023.07.27(要原题检测了,先占个坑,有时间再补)原题大意P3095[USACO13DEC]TheBessieShuffleS有\(n\)张牌,每次取出\(m\;(m<n)\)张牌进行置换操作。操......
  • 并查集优化 - 按大小合并时间复杂度证明
    并查集优化-按大小合并时间复杂度证明if(sz[b[x]].size()>sz[b[y]].size())swap(x,y);对于每个元素,当它当前所在的集合中,需要有其它大于该集合大小的集合,才会被遍历如果元素在一个大小\(1\)的集合中,会转移到大小\(2\)的集合中如果元素在一个大小\(2\)的集合中,会......
  • CF858C 题解
    洛谷链接&CF链接本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读。题目简述给你一个均为小写字母的字符串,如果它的子串同时满足:三个连着的辅音字母。这一段连着的辅音字母不是全部一样的。就认为它不合法。现在要求用最少的空格隔开这个字符串,使得它变成......
  • AT_agc022_a 题解
    洛谷链接&Atcoder链接本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读。题目简述给定字符串\(S\),仅包含互不相同的小写字母,你需要找到仅包含互不相同的小写字母的字符串中,第一个字典序比它大的字符串,如果找不到输出\(-1\)。(\(|S|\le26\))思路这篇题解......
  • 【题解】[2023牛客多校] Qu'est-ce Que C'est?
    题目传送门:[2023牛客多校]Qu'est-ceQueC'est?题意给定\(n,m\)构造\(a_{1},a_{2},...,a_{n}\),其中\(a_{i}\in[-m,m]\)之间取值。需要保证序列任意连续子段和都非负。\(0\leqn,m\leq5000\)分析记录合法序列的最小后缀。通过最小后缀来判断下一个数的取值范围。......
  • 并查集
    简述并查集其实是一个很有用的算法(至少我是这么认为的),很简单,代码也很好写,今天突然想写一下并查集。直接讲并查集不太好说,我们先看下面这一道题:洛谷P3367【模板】并查集【模板】并查集题目描述如题,现在有一个并查集,你需要完成合并和查询操作。输入格式第一行包含两个整数......
  • Codeforces Round 888 (Div. 3) 题解
    考场上\(7\)题做出来\(4\)题,最后几分钟才把D题调出来,但还是吃了不少罚时A.EscalatorConversations\(O(n)\)枚举即可,对于每个人计算需要的间隔台阶数是否在\((0,m)\)以内以及相差高度是否是\(k\)的倍数B.ParitySort显然,偶数和奇数是不可能产生交换操作的,而偶数......