洛谷

Codeforces


分治的题目,或者说分治的思想,是非常灵活多变的。

所以对我这种智商低的选手特别不友好

脑子不好使怎么办?多做题吧……


前置知识

线性基是你必须会的,不然这题不可做。

推荐再去看看洛谷P4151。


思路

看到异或最短路,显然线性基。

做题多一些的同学想必已经想到了“洛谷P4151 [WC2011]最大XOR和路径”了。

先考虑没有加边删边的做法:

  1. 做出原图的任意一棵生成树;
  2. 把每个非树边和树边形成的环丢进线性基里;
  3. 询问时把两点在树上的路径异或和丢进线性基里求最小异或和。

为什么要这样?见洛谷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 [分治,线性基,并查集]的更多相关文章

  1. Wanafly 挑战赛 14 E 无效位置 (线性基+并查集)

    Wanafly 挑战赛 14 E 无效位置 (线性基+并查集) 传送门:https://ac.nowcoder.com/acm/contest/81/#question 题意: n个数,m次操作 一个 ...

  2. CF 938G Shortest Path Queries

    又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治 这题算是经典应用了吧,假的动态图(可离线)问题 首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树 ...

  3. Codeforces 938G 线段树分治 线性基 可撤销并查集

    Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问 ...

  4. 【CF938G】Shortest Path Queries(线段树分治,并查集,线性基)

    [CF938G]Shortest Path Queries(线段树分治,并查集,线性基) 题面 CF 洛谷 题解 吼题啊. 对于每个边,我们用一个\(map\)维护它出现的时间, 发现询问单点,边的出 ...

  5. $CF938G\ Shortest\ Path\ Queries$ 线段树分治+线性基

    正解:线段树分治+线性基 解题报告: 传送门$QwQ$ 考虑如果只有操作3,就这题嘛$QwQ$ 欧克然后现在考虑加上了操作一操作二 于是就线段树分治鸭 首先线段树叶子节点是询问嘛这个不用说$QwQ$. ...

  6. CF938G Shortest Path Queries 和 CF576E Painting Edges

    这两道都用到了线段树分治和按秩合并可撤销并查集. Shortest Path Queries 给出一个连通带权无向图,边有边权,要求支持 q 个操作: x y d 在原图中加入一条 x 到 y 权值为 ...

  7. BZOJ.4184.shallot(线段树分治 线性基)

    BZOJ 裸的线段树分治+线性基,就是跑的巨慢_(:з」∠)_ . 不知道他们都写的什么=-= //41652kb 11920ms #include <map> #include < ...

  8. SCOI2016幸运数字(树剖/倍增/点分治+线性基)

    题目链接 loj luogu 题意 求树上路径最大点权异或和 自然想到(维护树上路径)+ (维护最大异或和) 那么有三种方法可以选择 1.树剖+线性基 2.倍增+线性基 3.点分治+线性基 至于线性基 ...

  9. Codeforces 938G(cdq分治+可撤销并查集+线性基)

    题意: 有一个无向连通图,支持三个操作: 1 x y d : 新建一条x和y的无向边,长度为d 2 x y    :删除x和y之间的无向边 3 x y    :询问x到y的所有路径中(可以绕环)最短的 ...

随机推荐

  1. 学习 vue 源码 -- 响应式原理

    概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...

  2. GCC编译器原理(三)------编译原理三:编译过程(2-2)---编译之语法分析

    2.2 语法分析 语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树(Syntax Tree).整个分析过程采用了上下文无关语法(Context-free G ...

  3. asp.net上传图片,上传图片

    想必很多人工作中经常需要实现上传图片的功能. 先引用此插件 http://files.cnblogs.com/files/hmYao/jquery-form.js. 前台代码 <form dat ...

  4. java保留字

    //*********java保留字 //*********访问控制 private //私有 protected //受保护 public //公共 //*********类.方法和变量修饰符 ab ...

  5. C++ 函数的重载和参数默认值

    函数的重载和参数默认值视频教程 函数的重载注意事项: 只会根据三项内容进行重载:参数的个数.参数的类型.参数的顺序 参数默认值: 参数的默认值可以在函数的定义中也可以在函数的声明中,但不能同时有 从第 ...

  6. Ubuntu下使用Windows应用程序集锦

    QQ & TIM:Wine-QQ-TIM 待续……

  7. call,apply和bind的区别

    call,apply继承并调用该函数,bind继承后未调用函数. var React = require('react'); //核心 var ReactDom = require('react-do ...

  8. 经典视觉SLAM框架

    经典视觉SLAM框架 整个视觉SLAM流程包括以下步骤: 1. 传感器信息读取.在视觉SLAM中主要为相机图像信息的读取和预处理. 2. 视觉里程计(Visual Odometry,VO).视觉里程计 ...

  9. valueForKeyPath用途

    可能大家对- (id)valueForKeyPath:(NSString *)keyPath方法不是很了解. 其实这个方法非常的强大,举个例子: NSArray *array = @[@"n ...

  10. 20165221 JAVA第四周学习心得

    教材内容总结 子类与继承 子类与父类 定义的标准格式为 class 子类名 extends 父类名 { ... } 如果一个类的声明中,没有使用extends关键字,则默认为Object类. 子类的继 ...