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 ...
随机推荐
- Zotero入门精通
一.Zotero简介 Zotero作为一款协助科研工作者收集.管理以及引用研究资源的免费软件,如今已被广泛使用.此篇使用说明主要分享引用研究资源功能,其中研究资源可以包括期刊.书籍等各类文献和网页.图 ...
- yii2自定义验证规则
- 反射2-spring boot jpa 注入model即实现查询
spring boot jpa 使用方法:将对应的model类注入即可// fixed parameter type private Specification<TargetModel> ...
- 游戏设计模式——Unity事件队列(纪念京阿尼事件)
“对消息或事件的发送与受理进行时间上的解耦.” 在游戏开发过程中,经常会出现不同板块之间的信息交流,或是存在“当...,就...”的情况,事件队列编程模式可以有效解决消息传递中产生的脚本耦合问题,让同 ...
- Selenium+java - 使用csv文件做数据驱动
前言 早期我们使用TestNG 来做数据驱动进行测试,测试数据是写在测试用例脚本中.这会使得测试脚本的维护工作量很大.因此我们可以将测试的数据和脚本分开. 而我们经常使用会使用csv文件来做为导出数据 ...
- C#实现数据回滚,A事件和B事件同时执行,其中任何一个事件执行失败,都会返回失败
/// <summary> /// 执行数据库回滚操作,用于sql语句执行失败后,恢复执行前的数据 /// </summary> /// <param name=&quo ...
- 一个有用的排序函数,array_multisort(),下面的一个用法是根据二维数组里的一个字段值的大小,对该二维数组进行重新排序
从二维数组$cashes中取出一列 'store_id'(二维数组中的每个一维数组都有的字段),按照这个的大小排序,对二维数组$caches里面的一维数组进行重新排序 实际应用如下 想让相同部门的排在 ...
- 遍历json数据的几种方式
json(JavaScript Object Notation),json是一种多用于存储和交换文本信息的语法.他能够进行数据的传输,通常和ajax一起使用.它具有体积小.速度快,易解析等诸多优点. ...
- 【HDU6037】Expectation Division(动态规划,搜索)
[HDU6037]Expectation Division(动态规划,搜索) 题面 Vjudge 你有一个数\(n\),\(n\le 10^{24}\),为了方便会告诉你\(n\)分解之后有\(m\) ...
- Android应用图标尺寸以及drawable(ldpi、mdpi、hdpi、xhdpi、xxhdpi)
应用图标: L DPI ( Low Density Screen,120 DPI ),其图标大小为 36 x 36 px M DPI ( Medium Density Screen, 160 DPI ...