CF 938G Shortest Path Queries
又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治
这题算是经典应用了吧,假的动态图(可离线)问题
首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树里,考虑如何维护答案
初步的想,图上两点间异或最小值,和最大值类似。先求出一棵生成树,然后把环扔进线性基里,每次查询两点间异或值之后再扔进线性基里求最小值即可
正确性的话,因为这样环一定是有树边+非树边构成的,我们可以在任意一个点走到一个环再绕回来,中间重复走的树边因为走了两次相当于没有影响
然后我们惊喜地发现,线性基是支持撤销的,而且两点间异或值可以用带撤销并查集来做,然后线段树分治的时候回溯的时候撤销即可
然后就做完了,总复杂度\(O(n\log^2 n)\),足以通过此题
说句题外话,那个维护两点间距离的并查集我本来准备用我的可换根并查集来写了,但是发现换根操作的逆过程(即撤销)比较难写,所以最后不得已向差分低头
#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<utility>
#include<map>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define mp make_pair
using namespace std;
typedef pair <int,int> pi;
const int N=200005,R=30;
struct edge
{
int x,y,v,s,t;
}e[N<<1]; int n,m,q,opt,cnt,tim,x,y,qx[N],qy[N],top; map <pi,int> Hash;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=0)++=ch))
char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[15];
public:
inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
RI ptop=0; while (pt[++ptop]=x%10,x/=10);
while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void flush(void)
{
fwrite(Fout,1,Ftop-Fout,stdout);
}
#undef tc
#undef pc
}F;
class Linear_Basis
{
private:
int r[100][R];
public:
inline void insert(int x,CI dep)
{
for (RI i=R-1;~i;--i) if ((x>>i)&1)
if (r[dep][i]) x^=r[dep][i]; else
{
r[dep][i]=x; for (RI j=R-1;j>i;--j)
if ((r[dep][j]>>i)&1) r[dep][j]^=x; break;
}
}
inline int query(int ret,CI dep)
{
for (RI i=R-1;~i;--i) if (r[dep][i]) ret=min(ret,ret^r[dep][i]); return ret;
}
inline void copy(CI dep)
{
for (RI i=0;i<R;++i) r[dep][i]=r[dep-1][i];
}
}LB;
class UnionFindSet
{
private:
int fa[N],size[N],val[N],stk[N];
inline int getfa(CI x)
{
return x!=fa[x]?getfa(fa[x]):x;
}
public:
inline void init(void)
{
for (RI i=1;i<=n;++i) fa[i]=i,size[i]=1;
}
inline int getval(CI x)
{
return x!=fa[x]?val[x]^getval(fa[x]):0;
}
inline int identify(CI x,CI y)
{
return getfa(x)==getfa(y);
}
inline void Union(int x,int y,CI v)
{
x=getfa(x); y=getfa(y); if (size[x]>size[y]) swap(x,y);
fa[x]=y; size[y]+=size[x]==size[y]; val[x]=v^val[x]; stk[++top]=x;
}
inline void revoke(CI tar)
{
int x; while (top>tar) x=stk[top--],size[fa[x]]-=(size[fa[x]]==size[x]+1),val[x]=0,fa[x]=x;
}
}S;
class Segment_Tree
{
private:
vector <int> pv[N<<2];
inline void expand(CI now,CI dep)
{
for (vector <int>::iterator it=pv[now].begin();it!=pv[now].end();++it)
{
int x=e[*it].x,y=e[*it].y,v=S.getval(x)^S.getval(y)^e[*it].v;
if (S.identify(x,y)) LB.insert(v,dep); else S.Union(x,y,v);
}
}
inline void calc(CI now,CI pos,CI dep,CI tp)
{
int ans=S.getval(qx[pos])^S.getval(qy[pos]);
F.write(LB.query(ans,dep)); S.revoke(tp);
}
public:
#define TN CI now=1,CI l=1,CI r=tim
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
inline void insert(CI beg,CI end,CI pos,TN)
{
if (beg>end) return; if (beg<=l&&r<=end) return (void)(pv[now].push_back(pos)); int mid=l+r>>1;
if (beg<=mid) insert(beg,end,pos,LS); if (end>mid) insert(beg,end,pos,RS);
}
inline void solve(TN,CI dep=1)
{
int tp=top; expand(now,dep); if (l==r) return calc(now,l,dep,tp); int mid=l+r>>1;
LB.copy(dep+1); solve(LS,dep+1); LB.copy(dep+1); solve(RS,dep+1); S.revoke(tp);
}
#undef TN
#undef LS
#undef RS
}SEG;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),i=1;i<=m;++i)
F.read(e[i].x),F.read(e[i].y),F.read(e[i].v),
e[i].s=1,e[i].t=-1,Hash[mp(e[i].x,e[i].y)]=i;
for (cnt=m,F.read(q),i=1;i<=q;++i)
{
F.read(opt); F.read(x); F.read(y); switch (opt)
{
case 1:
e[++cnt].x=x; e[cnt].y=y; F.read(e[cnt].v);
e[cnt].s=tim+1; e[cnt].t=-1; Hash[mp(x,y)]=cnt; break;
case 2:
e[Hash[mp(x,y)]].t=tim; Hash[mp(x,y)]=0; break;
case 3:
qx[++tim]=x; qy[tim]=y; break;
}
}
if (!tim) return 0; for (i=1;i<=cnt;++i) if (!~e[i].t) e[i].t=tim;
for (i=1;i<=cnt;++i) SEG.insert(e[i].s,e[i].t,i);
return S.init(),SEG.solve(),F.flush(),0;
}
CF 938G Shortest Path Queries的更多相关文章
- Codeforces 938G Shortest Path Queries [分治,线性基,并查集]
洛谷 Codeforces 分治的题目,或者说分治的思想,是非常灵活多变的. 所以对我这种智商低的选手特别不友好 脑子不好使怎么办?多做题吧-- 前置知识 线性基是你必须会的,不然这题不可做. 推荐再 ...
- 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)
[CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...
- CF938G Shortest Path Queries 和 CF576E Painting Edges
这两道都用到了线段树分治和按秩合并可撤销并查集. Shortest Path Queries 给出一个连通带权无向图,边有边权,要求支持 q 个操作: x y d 在原图中加入一条 x 到 y 权值为 ...
- CF938G Shortest Path Queries
首先只有询问的话就是个WC的题,线性基+生成树搞一搞就行. 进一步,考虑如果修改操作只有加边怎么做. 好像也没有什么变化,只不过需要在线地往线性基里插入东西而已. 删边呢? 注意到线性基这个玩意是不支 ...
- $CF938G\ Shortest\ Path\ Queries$ 线段树分治+线性基
正解:线段树分治+线性基 解题报告: 传送门$QwQ$ 考虑如果只有操作3,就这题嘛$QwQ$ 欧克然后现在考虑加上了操作一操作二 于是就线段树分治鸭 首先线段树叶子节点是询问嘛这个不用说$QwQ$. ...
- 题解 CF938G 【Shortest Path Queries】
题目让我们维护一个连通无向图,边有边权,支持加边删边和询问从\(x\)到\(y\)的异或最短路. 考虑到有删边这样的撤销操作,那么用线段树分治来实现,用线段树来维护询问的时间轴. 将每一条边的出现时间 ...
- Shortest Path(思维,dfs)
Shortest Path Accepts: 40 Submissions: 610 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: ...
- Shortest Path
Shortest Path Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)T ...
- 74(2B)Shortest Path (hdu 5636) (Floyd)
Shortest Path Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)T ...
随机推荐
- webpack打包优化实践
事情缘由 近段时间在做基于scratch3.0的改造项目.基于scratch-gui改造,项目本身已经很大了,然后里面还要用到scratch-blocks,scratch-vm,scratch-ren ...
- Python爬虫教程-使用chardet
Spider-03-使用chardet继续学习python爬虫,我们经常出现解码问题,因为所有的页面编码都不统一,我们使用chardet检测页面的编码,尽可能的减少编码问题的出现 网页编码问题解决使用 ...
- C语言程序设计100例之(9):生理周期
例9 生理周期 问题描述 人生来就有三个生理周期,分别为体力.感情和智力周期,它们的周期长度为 23 天.28 天和33 天.每一个周期中有一天是高峰.在高峰这天,人会在相应的方面表现出色.例如 ...
- window.onload在文档加载完成后执行
验证a .b两点疑惑: a.<script src="./main.js"></script>中的window.onload是在html全部加载完了才执行, ...
- Swiper实现轮播图效果
为了实现轮播图(carousel)效果或左右滑动显示不同的内容,我们采用Swiper来实现. 需要引入swiper.min.css和swiper.min.js,文件可从https://github.c ...
- 07-selenium、PhantomJS(无头浏览器)
selenium(自动化测试工具可用于在爬虫中解决js动态加载问题) 简介(本质就是模仿浏览器工作) Selenium 是什么?一句话,自动化测试工具.它支持各种浏览器,包括 Chrome,Safar ...
- json数据格式与字典数据类型之间的相互转换
import json class HandleJson: ''' 定义一个json格式数据处理类 ''' @staticmethod def loads_data(data): ''' 将json数 ...
- Jmeter-Java请求实战
1.1. jmeter-java插件实现接口测试 (linux /mysql/rabbit-mq) 本次需要准备环境 Eclipse+jdk8 Jmeter Python 1.1.1. Rabbit- ...
- oracle查找某个字符在字符串中的个数的技巧
Oracle没有提供查找某个字符在字符串中出现次数的函数,当遇到这样的需求的时候,我们只能使用另外的方法去实现. 简单的思路就是,假设有个字符串str,然后里面有n个[a]字符,当把这n个[a]字符去 ...
- [转]使用IConfigureNamedOptions和ConfigureAll配置命名选项
这是我上一篇关于在ASP.NET Core 2.x中使用多个强类型设置实例的后续文章.在文章的结尾,我介绍了命名选项的概念,该选项已添加到ASP.NET Core 2.0中.在本文中,我将详细介绍如何 ...