CF938G Shortest Path Queries 和 CF576E Painting Edges
这两道都用到了线段树分治和按秩合并可撤销并查集。
Shortest Path Queries
给出一个连通带权无向图,边有边权,要求支持 q 个操作:
- x y d 在原图中加入一条 x 到 y 权值为 d 的边
- x y 把图中 x 到 y 的边删掉
- x y 表示询问 x 到 y 的异或最短路
保证任意操作后原图连通无重边自环且操作均合法
n,m,q≤200000
题解
与WC2011 最大XOR和路径一样,先考虑没有加边删边的做法
- 做出原图的任意一棵生成树
- 把每个非树边和树边形成的环丢进线性基里
- 询问时把两点在树上的路径异或和丢进线性基里求最小异或和
有加边删边呢?线段树分治即可。
每到一个点,把边都连上,然后分治左右。退出时撤销即可。
需要用到按秩合并并查集以支持快速查询和撤销操作。并查集在合并两个点时,将儿子节点压入栈,撤销时直接把儿子节点的贡献删除即可。
#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define co const
#define il inline
typedef long long LL;
co int N=200000+10;
struct edge {int u,v,w,l,r;}e[N<<1];
vector<edge> g[N<<2];
map<pair<int,int>,int> mp;
int val[N],fa[N],siz[N];
int stk[N<<1],top;
#define lc (x<<1)
#define rc (x<<1|1)
void update(int x,int l,int r,co edge&e){
if(e.l>e.r) return; // edit 1
if(e.l<=l&&r<=e.r)
return g[x].push_back(e);
int mid=(l+r)>>1;
if(e.l<=mid) update(lc,l,mid,e);
if(e.r>mid) update(rc,mid+1,r,e);
}
struct ask {int u,v;}qst[N];
int bas[20][30];
int find_fa(int x){
return fa[x]==x?x:find_fa(fa[x]);
}
int find_val(int x){
return fa[x]==x?0:val[x]^find_val(fa[x]);
}
void insert(int x,int dep){
for(int i=0;i<(int)g[x].size();++i){
int u=g[x][i].u,v=g[x][i].v,w=g[x][i].w;
int fu=find_fa(u),fv=find_fa(v);
if(fu!=fv){
if(siz[fu]>siz[fv]) swap(fu,fv),swap(u,v);
fa[fu]=fv,siz[fv]+=siz[fu],val[fu]=find_val(u)^find_val(v)^w;
stk[++top]=fu;
}
else{
w^=find_val(u)^find_val(v);
for(int j=29;j>=0;--j)if(w>>j&1){
if(!bas[dep][j]) {bas[dep][j]=w;break;}
w^=bas[dep][j];
}
}
}
}
void reset(int to){
for(;top>to;--top){
int u=stk[top];
siz[fa[u]]-=siz[u],val[u]=0,fa[u]=u;
}
}
void solve(int x,int l,int r,int dep){
int tmp=top;
insert(x,dep);
if(l==r){
int u=qst[l].u,v=qst[l].v;
int ans=find_val(u)^find_val(v);
for(int i=29;i>=0;--i)if(ans>>i&1)
if(bas[dep][i]) ans^=bas[dep][i];
printf("%d\n",ans);
return reset(tmp);
}
int mid=(l+r)>>1;
copy(bas[dep],bas[dep]+30,bas[dep+1]),solve(lc,l,mid,dep+1);
copy(bas[dep],bas[dep]+30,bas[dep+1]),solve(rc,mid+1,r,dep+1);
reset(tmp);
}
int main(){
int n=read<int>(),m=read<int>();
int ecnt=0;
for(int i=1;i<=m;++i){
int u=read<int>(),v=read<int>(),w=read<int>();
e[++ecnt]=(edge){u,v,w,1,-1},mp[make_pair(u,v)]=ecnt;
}
int q=read<int>();
int totq=0;
for(int i=1;i<=q;++i){
int opt=read<int>();
if(opt==1){ // add
int x=read<int>(),y=read<int>(),d=read<int>();
e[++ecnt]=(edge){x,y,d,totq+1,-1},mp[make_pair(x,y)]=ecnt;
}
else if(opt==2){ // remove
int x=read<int>(),y=read<int>();
e[mp[make_pair(x,y)]].r=totq,mp[make_pair(x,y)]=0;
}
else{ // calculate
int x=read<int>(),y=read<int>();
qst[++totq]=(ask){x,y};
}
}
for(int i=1;i<=ecnt;++i){
if(e[i].r==-1) e[i].r=totq;
update(1,1,totq,e[i]);
}
for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
solve(1,1,totq,0);
return 0;
}
我写的线段树是需要判断插入合不合法的,好几次因为这个RE了。
Painting Edges
与BZOJ4025 二分图类似,这道题可以用并查集来维护奇环。
但是这道题我们并不知道每条边的具体存在时间,因此我们这样:
假设每次修改都生效,我们把每条边的第一种颜色的存在时间加入分治结构(其实就是线段树)
DFS的过程中,如果DFS到一个叶节点时,判断当前修改能否执行,这样我们就得到了这条边在下一个时间段的颜色,然后将这条边的下一个时间段加入分治结构
时间复杂度O(q log q log n)。话说我这两道题写的并查集都是假的也没人来卡我。
#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0,w=1;char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*w;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define co const
#define il inline
typedef long long LL;
co int N=500000+10;
int n,m,K,Q;
struct edge {int u,v,c;}e[N];
struct ask {int e,c,r;}q[N];
int last[N];
int stk[N],col[N],top;
struct Disj{
int fa[N],val[N],siz[N];
int find_fa(int x){
return fa[x]==x?x:find_fa(fa[x]);
}
int find_val(int x){
return fa[x]==x?0:val[x]^find_val(fa[x]);
}
void merge(int c,int x,int y){
int fx=find_fa(x),fy=find_fa(y);
if(fx==fy) return;
if(siz[fx]>siz[fy]) swap(fx,fy),swap(x,y);
fa[fx]=fy,siz[fy]+=siz[fx],val[fx]=find_val(x)^1^find_val(y);
stk[++top]=fx,col[top]=c;
}
}d[51];
void reset(int tmp){
for(;top>tmp;--top){
int u=stk[top],c=col[top];
d[c].siz[d[c].fa[u]]-=d[c].siz[u],d[c].fa[u]=u,d[c].val[u]=0;
}
}
vector<pair<int,int> > g[N<<2];
#define lc (x<<1)
#define rc (x<<1|1)
void insert(int x,int l,int r,int ql,int qr,co pair<int,int>&v){
if(ql>qr) return;
if(ql<=l&&r<=qr)
return g[x].push_back(v);
int mid=(l+r)>>1;
if(ql<=mid) insert(lc,l,mid,ql,qr,v);
if(qr>mid) insert(rc,mid+1,r,ql,qr,v);
}
void solve(int x,int l,int r){
int tmp=top;
for(int i=0;i<(int)g[x].size();++i){
int e=g[x][i].first,c=g[x][i].second;
d[c].merge(c,::e[e].u,::e[e].v);
}
if(l==r){
int u=e[q[l].e].u,v=e[q[l].e].v,c=q[l].c;
if(d[c].find_fa(u)!=d[c].find_fa(v) or d[c].find_val(u)!=d[c].find_val(v))
puts("YES"),e[q[l].e].c=q[l].c;
else puts("NO");
insert(1,1,Q,l+1,q[l].r,make_pair(q[l].e,e[q[l].e].c));
return reset(tmp);
}
int mid=(l+r)>>1;
solve(lc,l,mid),solve(rc,mid+1,r);
reset(tmp);
}
int main(){
read(n),read(m),read(K),read(Q);
for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v);
for(int i=1;i<=Q;++i){
int id=read<int>(),c=read<int>();
if(last[id]) q[last[id]].r=i-1;
q[i]=(ask){id,c,Q},last[id]=i;
}
for(int c=1;c<=K;++c)
for(int i=1;i<=n;++i) d[c].fa[i]=i,d[c].siz[i]=1;
solve(1,1,Q);
return 0;
}
CF938G Shortest Path Queries 和 CF576E Painting Edges的更多相关文章
- CF938G Shortest Path Queries
首先只有询问的话就是个WC的题,线性基+生成树搞一搞就行. 进一步,考虑如果修改操作只有加边怎么做. 好像也没有什么变化,只不过需要在线地往线性基里插入东西而已. 删边呢? 注意到线性基这个玩意是不支 ...
- $CF938G\ Shortest\ Path\ Queries$ 线段树分治+线性基
正解:线段树分治+线性基 解题报告: 传送门$QwQ$ 考虑如果只有操作3,就这题嘛$QwQ$ 欧克然后现在考虑加上了操作一操作二 于是就线段树分治鸭 首先线段树叶子节点是询问嘛这个不用说$QwQ$. ...
- 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)
[CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...
- 题解 CF938G 【Shortest Path Queries】
题目让我们维护一个连通无向图,边有边权,支持加边删边和询问从\(x\)到\(y\)的异或最短路. 考虑到有删边这样的撤销操作,那么用线段树分治来实现,用线段树来维护询问的时间轴. 将每一条边的出现时间 ...
- Codeforces 938G Shortest Path Queries [分治,线性基,并查集]
洛谷 Codeforces 分治的题目,或者说分治的思想,是非常灵活多变的. 所以对我这种智商低的选手特别不友好 脑子不好使怎么办?多做题吧-- 前置知识 线性基是你必须会的,不然这题不可做. 推荐再 ...
- CF576E Painting Edges
首先,有一个很暴力的nk的做法,就是对每种颜色分别开棵lct来维护. 实际上,有复杂度与k无关的做法. 感觉和bzoj4025二分图那个题的区别就在于这个题是边dfs线段树边拆分区间.
- CF 938G Shortest Path Queries
又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治 这题算是经典应用了吧,假的动态图(可离线)问题 首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树 ...
- 【CF576E】Painting Edges 线段树按时间分治+并查集
[CF576E]Painting Edges 题意:给你一张n个点,m条边的无向图,每条边是k种颜色中的一种,满足所有颜色相同的边内部形成一个二分图.有q个询问,每次询问给出a,b代表将编号为a的边染 ...
- AOJ GRL_1_B: Shortest Path - Single Source Shortest Path (Negative Edges) (Bellman-Frod算法求负圈和单源最短路径)
题目链接: http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_1_B Single Source Shortest Path ...
随机推荐
- sync 简单实现 父子组件的双向绑定
这里主要是对vue文档中的sync进行一个再解释: 如果自己尝试的话,最好在已经使用emit 和prop实现了双向绑定的组件中尝试,以免出现不必要的错误: <!DOCTYPE html> ...
- 说说Java Web中的Web应用程序|乐字节
大家好,我是乐字节的小乐,今天接着上期文章<Javaweb的概念与C/S.B/S体系结构>继续往下介绍Java Web ,这次要说的是web应用程序. 1. Web 应用程序的工作原理 W ...
- 使用Mysql中的concat函数或正则匹配来快速批量生成用于执行的sql语句
背景介绍 今天需要给一张表里面补数据,需要按照行的维度进行update,如果是个别数据那么直接写update语句就可以了,但是场景要求的是将整表的数据进行update,要实现这个需求就不能只靠蛮力了, ...
- 24 AdminLTE 基础入门
1.AdminLTE介绍 AdminLTE是一款建立在bootstrap和jquery之上的开源的模板主题工具,它提供了一系列响应的.可重复使用的组件,并内置了多个模板页面:同时自适应多种屏幕分辨率, ...
- SQL Server 2019 新版本
2019 年 11 月 4 日,微软在美国奥兰多举办的 Ignite 大会上发布了关系型数据库 SQL Server 的新版本.与之前版本相比,新版本的 SQL Server 2019 具备以下重要功 ...
- tomcat启动时报No rules found matching 'Server/Service/Engine/Host/context'
tomcat是8.0版本. 在eclipse启动时,第二行报这个, 同时项目也没加载(tomcat启动成功了). 网上搜了半天, 试了半天, 没搞定. 最后不经意间发现: <Context do ...
- Java后台验证
前台的js验证,可以通过其他手段绕过,存在安全问题,所以引入Java后台进行验证 一.导入jar包 此为hibernate-validator jar包,进行Java后台验证使用,在Java 1.9及 ...
- BJFU-216-基于链式存储结构的图书信息表的修改
#include<stdio.h> #include<stdlib.h> #define MAX 100 typedef struct Book{ double no; cha ...
- js中Function引用类型中一些常见且有用的方法和属性
Function类型 函数由于是Function类型的一个实例,所以函数名就是一个指向函数对象的指针,不会与某个函数死死的连接在一起,这也导致了js中没有真正的重载,但好处是,函数对象可以作为另一个函 ...
- [终极巨坑]golang+vue开发日记【二】,登陆界面制作(一)
写在前面 本期内容是适合第一次使用vue或者golang开发的,内容会以实战的形式来讲解.看懂本段内容需要了解基础内容有html,css,最好可以看一下vue的基础.并且这里的每个知识点不可能详细解说 ...