失控的未来交通工具 (LOJ 508,带权并查集,数论)
LOJ 508 失控的未来交通工具 (带权并查集 + 数论)
$ solution: $
很综合的一道难题。看了让人不知所措,数据范围又大,题目描述又不清晰。只能说明这道题有很多性质,或者很多优化。
好了,回来讲正解。像这种在图上寻找路径,并且对路径取模,尤其还不是简单路径的题,基本上都和环的性质有关。因为环我们可以无限跑啊!而这一道题在环上路径长度取模,需要联系到一个结论(就是天天爱跑步):我可以加一个数 $ w $ 无限次,得到的数要对 $ m $ 取模,那么我最终能且仅能得到所有小于 $ m $ 的 $ gcd(w,m) $ 的倍数。知道了这个结论,本题就只需要再讨论三个问题了:
1. 当模数为奇数时:(这个不算正解一部分,只能有开导作用)
当 $ m $ 是个奇数时,我们可以在某条环上绕圈(注意单独的一条边也算环,因为我们可以来回跑)。对于某条长度为 $ w $ 的边 $ (x,y) $ ,如果我们想只让这一条边/环计入贡献,我们可以这样操作:(设询问里起点为 $ u $ 终点为 $ v $ )在 $ u, x $ 之间来回跑 $ m $ 次,然后 $ (x,y) $ 只跑一次,然后再在 $ y,v $ 之间跑 $ m $ 次。这样我们两边的长度因为取模被削去,只留下那中间一条边的贡献!
于是我们通过这条边能走的长度一定是 $ gcd (m, w) $ 的倍数。设 $ u,v $ 所在连通块有 $ k $ 条边,那么走的路径一定是 $ G = gcd (m, w_1 , w_2 , w_3 , ... ,w_k) $ 的倍数。于是我们开一个并查集维护连通块的 $ gcd $ 就好。当然我们首先还需要得到随便一条可以从 $ u $ 出发走到 $ v $ 的路径得长度,再从这个长度上添加或消除边的贡献,这个我们也可以用带权并查集维护。
2. 如何处理回答的信息
$ upd: $ 经 $ LYH $ 大佬的提醒,现将本段一些错误更新。
题目意思大致:我们有一条路径 $ (u,v) $ ,询问在 $ x, x + b, ... , x + (c − 1)\times b $ 这些数中,有多少数在原图上存在一条从 $ u $ 到 $ v $ 的路径,使得这个数和路径长度模 $ m $ 同余。
首先上面我们说了,我们用并查集维护一个值(暂时叫 $ G $ ),所有为 $ G $ 的倍数长度的路径我们都可以走到,于是题目所求就变成了:在 $ x, x + b, ... , x + (c − 1)\times b $ 这些数中,有多少数存在一条路径,使得这个数和这条路径长度的差是 $ G $ 的倍数。 (注意是差值)
可以知道当 $ c $ 很大的时候,我们肯定不能暴力判断。我们分析性质发现给出的数列是一个等差数列。所以这个数列在模 $ G $ 意义下一定是循环的,所以只需要找到第一个模 $ G $ 等于0的位置(就是说这个数是 $ G $ 的倍数)与循环节长度即可。即要求解同余方程 $ x + k\times b ≡ d[u]+d[v] ~~(mod~G) \quad -> \quad k\times b~+ y\times G = d[u]+d[v]-x $ 中 $ k $ 的值,这个用扩展欧几里得就好。而为了更契合扩展欧几里得,博主在代码里将这个同余方程的系数用 $ A,B,C $ 表示。
rg A=b,B=g[fx],G=gcd(A,B),C=(ll)d[u]+d[v]-w+B; //列出同余方程的系数
// 同余方程: C - x*A ≡ 0 (mod B) -> x*A + y*B = C (对应上文第二个式子!)
if(C%G){puts("0"); continue;} //方程无解,请参见扩欧的做法
rg x,y; A/=G; B/=G; C/=G; //请参见扩欧的解方程的步骤
exgcd(A,B,x,y); x=((ll)x*C%B+B)%B; //解出同余方程
if(x<c)ans=(c-1-x)/B+1; //注意我们找的是x的最小解,循环长度是模数B! (同余方程性质)
printf("%d\n",ans); //输出答案
3. 当模数为偶数:(这里才是正解)
这种情况可以用与奇数相似的做法,但是比奇数复杂得多!当 $ m $ 是个偶数时,我们可以在某条环上绕圈,但是我们绕 $ m $ 次之后会回到原点,这代表着我们只能得到 $ gcd(m,2\times w) $ 的倍数的路径长度(好好理解,这个需要自己思考),这个贡献和跑一遍含有这条边的环的贡献 $ gcd(m,k+m) $ 可能不一样!
所以我们考虑另一种做法:首先,我们依旧要维护边的贡献 $ gcd(m,2\times w) $ ,然后考虑将环的贡献也加进去。这个我们只要在并查集合并时维护,只有当两个节点同属于一个联通块时我们加入这条边才会产生环。然后我们只要维护这两个点到并查集的根的路径加上这条边产生的环的贡献即可。(以下图为例)我们此时并查集里根为1号结点,我们现在加入 $ w $ 边,我们只要维护 $ (a,b,w) $ 形成的环即可。虽然 $ w $ 边还可以和 $ (c,d) $ 形成环,但是我们之后也可以这样操作使的两个环相互转换:从2号结点跑环 $ w,a,b $ 回到2节点,再跑 $ (c,d,a,b) $ 回到2节点,这时 $ (a,b) $ 两条边跑了两次我们只要再 $ (a,b) $ 来回跑 $ m-2 $ 次即可消去 $ (a,b) $ 两边的的贡献,将环变成 $ (w,d,c) $ 。
同理我们还可以实现 $ (w,a,b) $ 到 $ (a,b,c,d) $ 环的转化,这里不做多讲,不会可以直接留言。这种联通块里加入边的贡献我们可以这样记录: $ G=gcd(G,d[u]+d[v]+w,2\times w) $ ,其中第一个是联通块原贡献值,第二个是包含 $ w $ 边的环的贡献,第三个是 $ w $ 这条边单独的贡献(在最开始讲了)。
于是我们用带权并查集维护节点间随意某一条路径,再从这个长度上用上述方法添加或消除环和边的贡献,这样可以得到一个合并后的总贡献 $ G $ 的所有倍数的路径长度。然后我们直接再按照第二个问题的做法做就可以了!
$ code: $
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define rg register int
using namespace std;
int n,m,q;
int d[1000005]; //并查集里面节点到其联通块的根的距离
int g[1000005]; //当前联通块的gcd,如天天爱跑步
int fa[1000005]; //并查集
inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
}
inline int gcd(int a,int b){
if(b==0)return a; return gcd(b,a%b);
}
inline int getfa(int x){
if(x==fa[x])return x;
rg to=getfa(fa[x]); //注意顺序,防止信息被覆盖
d[x]=(d[x]+d[fa[x]])%m; //更新当前节点到根的距离
return fa[x]=to; //最后再赋值
}
inline void exgcd(int a,int b,int &x,int &y){ //扩展欧几里得
if(b==0) x=1,y=0; //直接构造一组解
else exgcd(b,a%b,y,x),y-=a/b*x; //我们不需要求gcd,只要解方程就好
}
int main(){
n=qr(); m=qr(); q=qr();
for(rg i=1;i<=n;++i) fa[i]=i,g[i]=m;
for(rg i=1;i<=q;++i){
rg op=qr(),u=qr(),v=qr(),w=qr();
rg fx=getfa(u),fy=getfa(v); //查找根
if(op==1){
if(fx!=fy){ fa[fx]=fy; //合并
d[fx]=((ll)d[u]+d[v]+w)%m; //一个联通块只有一个根,所以另一个需要改距离
g[fy]=gcd(gcd(g[fx],g[fy]),2*w); //更新联通块的gcd
}else g[fx]=gcd(gcd(g[fx],((ll)d[u]+d[v]+w)%m),2*w); //新加了一个环,合并环的贡献
} else{ //请注意大小写!!!!!!
rg ans=0,b=qr()%g[fx],c=qr(); //读入
if(fx!=fy){puts("0"); continue;} //不连通意味着不能到达
rg A=b,B=g[fx],G=gcd(A,B),C=(ll)d[u]+d[v]-w+B; //列出同余方程的系数
// 同余方程: C + x*A = 0 (mod B) -> x*A + y*B = C (mod g[fx])
if(C%G){puts("0"); continue;} //方程无解,请参见扩欧的做法
rg x,y; A/=G; B/=G; C/=G; //请参见扩欧的解方程的步骤
exgcd(A,B,x,y); x=((ll)x*C%B+B)%B; //解出同余方程
if(x<c)ans=(c-1-x)/B+1; //注意我们找的是x的最小解,循环长度是模数B! (同余方程性质)
printf("%d\n",ans); //输出答案
}
}
return 0;
}
失控的未来交通工具 (LOJ 508,带权并查集,数论)的更多相关文章
- POJ 1703 Find them, Catch them(带权并查集)
传送门 Find them, Catch them Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 42463 Accep ...
- [NOIP摸你赛]Hzwer的陨石(带权并查集)
题目描述: 经过不懈的努力,Hzwer召唤了很多陨石.已知Hzwer的地图上共有n个区域,且一开始的时候第i个陨石掉在了第i个区域.有电力喷射背包的ndsf很自豪,他认为搬陨石很容易,所以他将一些区域 ...
- poj1417 带权并查集 + 背包 + 记录路径
True Liars Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 2713 Accepted: 868 Descrip ...
- poj1984 带权并查集(向量处理)
Navigation Nightmare Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 5939 Accepted: 2 ...
- 【BZOJ-4690】Never Wait For Weights 带权并查集
4690: Never Wait for Weights Time Limit: 15 Sec Memory Limit: 256 MBSubmit: 88 Solved: 41[Submit][ ...
- hdu3038(带权并查集)
题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=3038 题意: n表示有一个长度为n的数组, 接下来有m行形如x, y, d的输入, 表示 ...
- 洛谷OJ P1196 银河英雄传说(带权并查集)
题目描述 公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦 创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山 ...
- poj1984 带权并查集
题意:有多个点,在平面上位于坐标点上,给出一些关系,表示某个点在某个点的正东/西/南/北方向多少距离,然后给出一系列询问,表示在第几个关系给出后询问某两点的曼哈顿距离,或者未知则输出-1. 只要在元素 ...
- poj1611 带权并查集
题意:病毒蔓延,现在有 n 个人,其中 0 号被认为可能感染,然后给出多个社交圈,如果某个社交圈里有人被认为可能被感染,那么所有这个社交圈里的人都被认为可能被感染,现在问有多少人可能被感染. 带权并查 ...
随机推荐
- 树链剖分&咕咕咕了好久好久的qtree3
前言 显然qtree系列都是树链剖分辣 发现自己没有专门整理过树链剖分耶 辣么就把这篇博客魔改成树链剖分好辣(貌似除了树剖也没什么好写的) 正文 废话了辣么多终于开始了 一.树剖怎么写鸭 二.树剖有什 ...
- JVM参数设置-jdk8参数设置
JVM参数设置 1.基本参数 参数名称 含义 默认值 -Xms 初始堆大小 内存的1/64 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx ...
- tuple用法
1 tuple中的元素可以直接赋给相同个数的变量 tup1 = ('asfa',234) p, q = tup1 print(p) print(q) # asfa # 参考:https://www.r ...
- Golang基础(4):Go结构体
当我们要表示同一种数据类型时候,可以用到数组,切片和字典. 当我们要表示不同的数据类型呢?这时候就要用到结构体了 一:定义struct 关键字 type 和 struct 来定义结构体 type st ...
- Web UI自动化测试基础——元素定位(二)
本篇文章整理了元素定位的基础知识——多个元素定位方式. 一.多个元素定位方式简介 同单个元素定位方式相同,多个元素定位方式也有与之对应的8种方式,即id.name.class_name.tag_nam ...
- 虚拟机三种网络模式及Xshell与Centos7虚拟机连接
一.虚拟机的三种网络模式 1.桥接模式 a.该模式下的虚拟机可以上外网 b. 局域网之内的主机可以访问该虚拟机(做共享服务器使用) c. 该虚拟机可以和宿主机进行通信 d. 同一台主机相同模式下的虚拟 ...
- C语言I-博客作业05
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 C语言1博客作业05 我在这个课程的目标是 学会运用函数编代码 这个作业在那个具体方面帮助我实现目标 写C语言作业的时候,编代码的实践中 ...
- java 接入微信 spring boot 接入微信
1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- P1076寻宝
---恢复内容开始--- 这是2012noip普及组的一个模拟题,第一次得了50,看了题解后剪枝拿到100. N层楼,m个房间,逆时针排序.每个房间有一个指示牌,也可能有楼梯,找到第(上楼的第一个房间 ...
- Structs2下的MyFirstTest
1.这是<Struts2-权威指南>第二章的例子 2.博文主要说明在eclipse下如何创建一个struts2项目 3.实现功能:在login.jsp输入用户名和密码,若用户名为scott ...