Codeforces 938G Shortest Path Queries [分治,线性基,并查集]
分治的题目,或者说分治的思想,是非常灵活多变的。
所以对我这种智商低的选手特别不友好
脑子不好使怎么办?多做题吧……
前置知识
线性基是你必须会的,不然这题不可做。
推荐再去看看洛谷P4151。
思路
看到异或最短路,显然线性基。
做题多一些的同学想必已经想到了“洛谷P4151 [WC2011]最大XOR和路径”了。
先考虑没有加边删边的做法:
- 做出原图的任意一棵生成树;
- 把每个非树边和树边形成的环丢进线性基里;
- 询问时把两点在树上的路径异或和丢进线性基里求最小异或和。
为什么要这样?见洛谷P4151题解。
有加边呢?
其实差不了多少……加一条边就往线性基里丢个环就好了。
还有删边呢?
我们按时间分治,用线段树存储每一段新加了哪些边。
每到一个点,把边都连上,然后分治左右。退出时撤销即可。
然而此时图可能不连通,连边可能连了两棵不同的树,此时需要用可撤销并查集存储树的结构和每一个点到根的异或和。由于异或有着很好的性质,我们可以把两点连边改为两棵树的根连边,而答案不变。
具体是这样的:
设要在\(x,y\)之间连权值为\(w\)的边,它们所在的树的根是\(fx,fy\),则连一条\(fx-fy\),权值为$w\oplus getdis(x)\oplus getdis(y) $的边。
为什么对呢?你画画图推推式子就出来了。
然后就做完了~
代码
#include<bits/stdc++.h>
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define sz 202020
typedef long long ll;
template<typename T>
inline void read(T& t)
{
t=0;char f=0,ch=getchar();
double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.')
{
ch=getchar();
while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();
}
t=(f?-t:t);
}
template<typename T,typename... Args>
inline void read(T& t,Args&... args){read(t); read(args...);}
void file()
{
#ifndef ONLINE_JUDGE
freopen("a.txt","r",stdin);
#endif
}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;
int n,m,Q;
map<pii,int>M;
struct hh{int f,t,w;hh(int ff=0,int tt=0,int ww=0){f=ff,t=tt,w=ww;}}edge[sz<<1];
int bg[sz<<1],ed[sz<<1];
struct HH
{
int w[34];
void ins(int x)
{
drep(i,30,0) if (x&(1<<i))
{
if (!w[i]) return (void)(w[i]=x);
x^=w[i];
}
}
int query(int x){ drep(i,30,0) if ((x^w[i])<x) x^=w[i]; return x; }
}_;
vector<hh>v[sz<<2];
#define ls k<<1
#define rs k<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
void insert(int k,int l,int r,int x,int y,hh e)
{
if (x<=l&&r<=y) return (void)v[k].push_back(e);
int mid=(l+r)>>1;
if (x<=mid) insert(lson,x,y,e);
if (y>mid) insert(rson,x,y,e);
}
int qx[sz],qy[sz];
int fa[sz],f[sz],dep[sz];
int getfa(int x){return x==fa[x]?x:getfa(fa[x]);}
int getdis(int x){return x==fa[x]?0:f[x]^getdis(fa[x]);}
struct hhh{int x,y;bool s;};
void solve(int k,int l,int r,HH G)
{
stack<hhh>S;
rep(i,0,(int)v[k].size()-1)
{
int x=v[k][i].f,y=v[k][i].t,w=v[k][i].w;
int fx=getfa(x),fy=getfa(y);
w^=getdis(x)^getdis(y);
if (fx==fy) G.ins(w);
else
{
if (dep[fx]>dep[fy]) swap(fx,fy),swap(x,y);
hhh cur=(hhh){fx,fy,0};
fa[fx]=fy;f[fx]=w;
if (dep[fx]==dep[fy]) ++dep[fy],cur.s=1;
S.push(cur);
}
}
if (l==r) printf("%d\n",G.query(getdis(qx[l])^getdis(qy[l])));
else
{
int mid=(l+r)>>1;
solve(lson,G);solve(rson,G);
}
while (!S.empty()) f[fa[S.top().x]=S.top().x]=0,dep[S.top().y]-=S.top().s,S.pop();
}
int main()
{
file();
int x,y,z;
read(n,m);
rep(i,1,n) fa[i]=i;
int c=m,tim=1;
rep(i,1,m) read(x,y,z),M[MP(x,y)]=i,bg[i]=1,ed[i]=-1,edge[i]=hh(x,y,z);
read(Q);
rep(i,1,Q)
{
read(z,x,y);
if (z==1)
{
read(z);
M[MP(x,y)]=++c;bg[c]=tim;ed[c]=-1;
edge[c]=hh(x,y,z);
}
else if (z==2) ed[M[MP(x,y)]]=tim-1;
else qx[tim]=x,qy[tim]=y,++tim;
}
--tim;
rep(i,1,c) if (ed[i]==-1) ed[i]=tim;
rep(i,1,c) if (bg[i]<=ed[i]) insert(1,1,tim,bg[i],ed[i],edge[i]);
solve(1,1,tim,_);
return 0;
}
Codeforces 938G Shortest Path Queries [分治,线性基,并查集]的更多相关文章
- Wanafly 挑战赛 14 E 无效位置 (线性基+并查集)
Wanafly 挑战赛 14 E 无效位置 (线性基+并查集) 传送门:https://ac.nowcoder.com/acm/contest/81/#question 题意: n个数,m次操作 一个 ...
- CF 938G Shortest Path Queries
又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治 这题算是经典应用了吧,假的动态图(可离线)问题 首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树 ...
- Codeforces 938G 线段树分治 线性基 可撤销并查集
Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问 ...
- 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)
[CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...
- $CF938G\ Shortest\ Path\ Queries$ 线段树分治+线性基
正解:线段树分治+线性基 解题报告: 传送门$QwQ$ 考虑如果只有操作3,就这题嘛$QwQ$ 欧克然后现在考虑加上了操作一操作二 于是就线段树分治鸭 首先线段树叶子节点是询问嘛这个不用说$QwQ$. ...
- CF938G Shortest Path Queries 和 CF576E Painting Edges
这两道都用到了线段树分治和按秩合并可撤销并查集. Shortest Path Queries 给出一个连通带权无向图,边有边权,要求支持 q 个操作: x y d 在原图中加入一条 x 到 y 权值为 ...
- BZOJ.4184.shallot(线段树分治 线性基)
BZOJ 裸的线段树分治+线性基,就是跑的巨慢_(:з」∠)_ . 不知道他们都写的什么=-= //41652kb 11920ms #include <map> #include < ...
- SCOI2016幸运数字(树剖/倍增/点分治+线性基)
题目链接 loj luogu 题意 求树上路径最大点权异或和 自然想到(维护树上路径)+ (维护最大异或和) 那么有三种方法可以选择 1.树剖+线性基 2.倍增+线性基 3.点分治+线性基 至于线性基 ...
- Codeforces 938G(cdq分治+可撤销并查集+线性基)
题意: 有一个无向连通图,支持三个操作: 1 x y d : 新建一条x和y的无向边,长度为d 2 x y :删除x和y之间的无向边 3 x y :询问x到y的所有路径中(可以绕环)最短的 ...
随机推荐
- FFI
FFI概念 https://segmentfault.com/t/ffi/info FFI即Foreign Function Interface,外部函数调用接口,是一个语言提供的使用其他语言现有库( ...
- ****** 三十四 ******、软设笔记【存储器系统】-Cache存储器
Cache存储器 Cache(高速缓冲存储器) 高速缓冲存储器是位于主存与CPU之间的一级存储器,有静态存储芯片(SRAM)组成,容量比较小,速度比主存高得多,接近于CPU的速度,单位成本比内存高.C ...
- hadoop1.2.1的安装
前提:1.机器最好都做ssh免密登录,最后在启动hadoop的时候会简单很多 免密登录看免密登录 2.集群中的虚拟机最好都关闭防火墙,否则很麻烦 3集群中的虚拟机中必须安装jdk. 具体安装步骤如下: ...
- PHPcms 缓存的读取和设置
https://blog.csdn.net/huobobo124/article/details/76912632 1.PHPcms设置了保存和读取缓存的方法,其实现方法存储在PHPcms/lib/f ...
- Linux(Ubuntu)下安装jdk
一.下载 1)可以去官网下载:http://www.oracle.com/technetwork/java/javase/downloads/ea-jsp-142245.html,比较多,眼花~~· ...
- G - WiFi Password Gym - 101608G (异或思维题+曲尺)
题目链接:https://cn.vjudge.net/contest/285962#problem/G 题目大意:给你n和m,n代表有n个数,然后让你找出一个最长的区间,使得这个区间内的所有的数的‘’ ...
- delete 和 delete []的区别
(1). 针对简单类型 使用new分配后的不管是数组还是非数组形式内存空间用两种方式均可 如: int *a = new int[10]; delete a; delete [] a; ...
- Java类锁和对象锁
一.类锁和对象锁 二.使用注意 三.参考资料 一.类锁和对象锁 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class) 对象锁:在代码 ...
- python 中的exec
x = 10 expr = """ z = 30 sum = x + y + z print(sum) """ def func(): y ...
- Python Web开发框架Django
花了两周时间,利用工作间隙时间,开发了一个基于Django的项目任务管理Web应用.项目计划的实时动态,可以方便地被项目成员查看(^_^又重复发明轮子了).从前台到后台,好好折腾了一把,用到:HTML ...