题面

题目链接

题目描述

给定一张

N

N

N 个顶点

M

M

M 条边的无向图(顶点编号为

1

,

2

,

,

n

1,2,\ldots,n

1,2,…,n),每条边上带有权值。所有权值都可以分解成

2

a

×

3

b

2^a\times 3^b

2a×3b 的形式。

现在有

q

q

q 个询问,每次询问给定四个参数

u

,

v

,

a

u,v,a

u,v,a 和

b

b

b,请你求出是否存在一条顶点

u

u

u 到

v

v

v 之间的路径,使得路径依次经过的边上的权值的最小公倍数为

2

a

×

3

b

2^a\times 3^b

2a×3b。

注意:路径可以不是简单路径。

下面是一些可能有用的定义,如果与其它地方定义不同,在本题中以下面的定义为准:

路径:顶点序列

P

 ⁣

:

P

1

,

P

2

,

,

P

k

P \colon P_1,P_2,\ldots,P_k

P:P1​,P2​,…,Pk​ 是一条路径,当且仅当

k

2

k \geq 2

k≥2,且对于任意

1

i

<

k

1 \leq i < k

1≤i<k ,节点

P

i

P_i

Pi​ 和

P

i

+

1

P_{i+1}

Pi+1​ 之间都有边相连。

输入格式

输入文件的第一行包含两个整数

N

N

N 和

M

M

M,分别代表图的顶点数和边数。

接下来

M

M

M 行,每行包含四个整数

u

,

v

,

a

,

b

u,v,a,b

u,v,a,b 代表一条顶点

u

u

u 和

v

v

v 之间、权值为

2

a

×

3

b

2^a\times 3^b

2a×3b 的边。

接下来一行包含一个整数

q

q

q,代表询问数。

接下来

q

q

q 行,每行包含四个整数

u

,

v

,

a

u,v,a

u,v,a 和

b

b

b,代表一次询问。询问内容请参见问题描述。

输出格式

对于每次询问,如果存在满足条件的路径,则输出一行 Yes,否则输出一行 No(注意:第一个字母大写,其余字母小写)。

输入输出样例

输入 #1

4 5
1 2 1 3
1 3 1 2
1 4 2 1
2 4 3 2
3 4 2 2
5
1 4 3 3
4 2 2 3
1 3 2 2
2 3 2 2
1 3 4 4

输出 #1

Yes
Yes
Yes
No
No

说明/提示

1

n

,

q

5

×

1

0

4

1\le n,q\le 5\times 10^4

1≤n,q≤5×104,

1

m

1

0

5

1\leq m\leq 10^5

1≤m≤105,

0

a

,

b

1

0

9

0\leq a,b\leq 10^9

0≤a,b≤109。

题解

我们可以用这种办法判断是否满足条件:把权值为目标

l

c

m

\rm lcm

lcm 的因数——即边的

a

,

b

a,b

a,b 同时小于目标的

a

,

b

a,b

a,b——的边,保留下来。此时如果

u

u

u 和

v

v

v 在一个连通块内,同时该连通块所有边的

l

c

m

\rm lcm

lcm 等于目标

l

c

m

\rm lcm

lcm ,那么就存在满足条件的路径。路径可以不是简单路径,所以,只要是在同一连通块的边都可以走一次。既然是维护连通块,不妨用并查集

那么,首先把

a

a

a 和

b

b

b 离散化,因为有价值的仅仅是他们的相对大小关系。

然后,回滚莫队

我们把询问按照

a

a

a 分块,每个块内按照

b

b

b 从小到大排序。处理每个块的时候,并查集初始化,一条扫描线扫

b

b

b 端点。我们维护当前扫到的

b

b

b 的大小

R

R

R ,

R

R

R 初始为 0 。当

R

<

b

i

R<b_i

R<bi​ 的时候,R++ ,然后把

b

=

R

,

a

b=R,a\leq 块的左端

b=R,a≤块的左端 的边加进并查集,当

R

R

R 达到

b

i

b_i

bi​ 时,记录此时并查集的状态。接着处理

a

a

a 端点,把

a

(

,

a

i

]

,

b

b

i

a\in (块的左端,a_i],b\leq b_i

a∈(块的左端,ai​],b≤bi​ 的边加进并查集,查询该询问是否有解。最后把并查集退回到之前记录的状态,保留

R

R

R ,计算块内的下一个询问。

令块大小为

S

S

S ,那么时间应该是

q

×

S

×

α

(

n

)

+

M

S

×

M

×

α

(

n

)

(

2

×

)

M

α

(

n

)

q

q\times S\times\alpha(n)+\frac{M}{S}\times M\times\alpha(n)\geq(2\times)M\alpha(n)\sqrt{q}

q×S×α(n)+SM​×M×α(n)≥(2×)Mα(n)q

​,当且仅当

S

=

M

2

q

S=\sqrt{\frac{M^2}{q}}

S=qM2​

​ 时取最小,最大数据下

S

S

S 可取

447

447

447 。

关于按

a

a

a 分块,有一个难点。那就是,当大量的边都有着同样的

a

a

a 抱团的时候,我们又不得不把所有的相同值的边都考虑,于是,直接按

a

a

a 的值分块,无疑会慢到极致。但是,我们会发现,设块的左端点为

l

i

l_i

li​ ,所有

a

=

l

i

a=l_i

a=li​ 的边只会被添加一次,是在扩展

R

R

R 的时候被添加的。所以,我们把边的数量

S

\geq S

≥S 的

a

a

a 值单独算一个,就可以避免这个问题。

时间复杂度

O

(

M

α

(

n

)

q

)

\rm O(M\alpha_{^{(n)}}\sqrt{q})

O(Mα(n)​q

​) ,跑上几百毫秒是可以的。

CODE

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 400005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define SQ 447
LL read() {
LL f=1,x=0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f*x;
}
void putpos(LL x) {
if(!x) return ;
putpos(x/10); putchar('0'+(x%10));
}
void putnum(LL x) {
if(!x) putchar('0');
else if(x < 0) putchar('-'),putpos(-x);
else putpos(x);
}
int n,m,s,o,k,Q;
int U[MAXN],V[MAXN],w2[MAXN],w3[MAXN];
int b1[MAXN],b2[MAXN],cn1,cn2,nm1,nm2;
map<int,int> mp1,mp2;
vector<int> bu[MAXN],rq[MAXN];
int bl[MAXN],br[MAXN],belong[MAXN],cnt;
struct it{
int u,v,l,r,id;it(){u=v=l=r=id=0;}
it(int U,int V,int A,int B,int I){u=U;v=V;l=A;r=B;id=I;}
it(int U,int ma,int mb){u=U;l=ma;r=mb;v=0;id=0;}
}q[MAXN];
vector<it> B[MAXN];
int cnq;
bool as[MAXN];
bool cmp(it a,it b) {
return a.r < b.r;
}
bool F_st;
stack<it> st;
int fa[MAXN],siz[MAXN],mx1[MAXN],mx2[MAXN];
int findf(int x) {return x==fa[x] ? x:(findf(fa[x]));}
void unionSet(int a,int b,int m2,int m3) {
int u = findf(a),v = findf(b);
if(siz[u] > siz[v]) swap(u,v),swap(a,b);
if(u == v) {
if(F_st)st.push(it(u,mx1[u],mx2[u]));
mx1[u] = max(mx1[u],m2);
mx2[u] = max(mx2[u],m3);
}
else {
if(F_st)st.push(it(u,mx1[v],mx2[v]));
siz[v] += siz[u]; fa[u] = v;
mx1[v] = max(mx1[v],max(mx1[u],m2));
mx2[v] = max(mx2[v],max(mx2[u],m3));
}return ;
}
void Back(it t) {
int u = t.u;
if(fa[u] == u) {
mx1[u] = t.l; mx2[u] = t.r;
}
else {
int v = fa[u];
mx1[v] = t.l; mx2[v] = t.r;
siz[v] -= siz[u]; fa[u] = u;
}return ;
}
void adda(int ad,int rr) {
for(int i = 0;i < (int)bu[ad].size();i ++) {
int x = bu[ad][i];
if(w3[x] <= rr) {
unionSet(U[x],V[x],w2[x],w3[x]);
}
}return ;
}
void addb(int ad,int rr) {
for(int i = 0;i < (int)rq[ad].size();i ++) {
int x = rq[ad][i];
if(w2[x] <= rr) {
unionSet(U[x],V[x],w2[x],w3[x]);
}
}return ;
}
void query(int x,int y,int &aa,int &bb) {
aa = 0; bb = 0;
if(findf(x) != findf(y)) return ;
int v = findf(x);
aa = mx1[v]; bb = mx2[v];
return ;
}
int main() {
// freopen("lcm.in","r",stdin);
// freopen("lcm.out","w",stdout);
n = read();m = read();
for(int i = 1;i <= m;i ++) {
U[i] = read();
V[i] = read();
w2[i] = read();
w3[i] = read();
b1[++ cn1] = w2[i];
b2[++ cn2] = w3[i];
}
sort(b1 + 1,b1 + 1 + m);
sort(b2 + 1,b2 + 1 + m);
for(int i = 1;i <= m;i ++) {
if(i == 1 || b1[i] > b1[i-1]) mp1[b1[i]] = ++ nm1;
if(i == 1 || b2[i] > b2[i-1]) mp2[b2[i]] = ++ nm2;
}
for(int i = 1;i <= m;i ++) {
w2[i] = mp1[w2[i]]; w3[i] = mp2[w3[i]];
bu[w2[i]].push_back(i);
rq[w3[i]].push_back(i);
}
int ct = 0,prl = 0;
for(int i = 1;i <= nm1;i ++) {
ct += bu[i].size();
belong[i] = cnt + 1;
if(ct >= SQ || i == nm1 || (int)bu[i+1].size() >= SQ) {
cnt ++;
bl[cnt] = prl + 1;
prl = br[cnt] = i;
ct = 0;
}
}
Q = read();
for(int i = 1;i <= Q;i ++) {
s = read();o = read();
int aa = mp1[read()],bb = mp2[read()];
if(aa && bb) {
it t = it(s,o,aa,bb,i);
q[++ cnq] = t;
B[belong[t.l]].push_back(t);
}
}
for(int i = 1;i <= cnt;i ++) {
sort(B[i].begin(),B[i].end(),cmp);
for(int j = 1;j <= n;j ++) {
fa[j] = j; siz[j] = 1;
mx1[j] = 0; mx2[j] = 0;
}
while(!st.empty()) st.pop();
int R = 0;
for(int j = 0;j < (int)B[i].size();j ++) {
it x = B[i][j];
while(R < x.r) addb(++ R,bl[i]);
int L = bl[i];
F_st = 1;
while(L < x.l) adda(++ L,x.r);
F_st = 0;
int sa,sb; query(x.u,x.v,sa,sb);
as[x.id] = (sa == x.l && sb == x.r);
while(!st.empty()) {
it t = st.top();st.pop();
Back(t);
}
}
}
for(int i = 1;i <= Q;i ++) {
printf(as[i] ? "Yes\n":"No\n");
}
return 0;
}
/*
q*S*log + M/S*M*log >= 2*sqrt{q}*M*log
q*S = M/S*M => S = sqrt(M*M/q) = sqrt(2e5) = 447
*/

[HNOI2016]最小公倍数 (可回退并查集,回滚莫队)的更多相关文章

  1. HihoCoder 1629 Graph (2017 ACM-ICPC 北京区域赛 C题,回滚莫队 + 启发式合并 + 可撤销并查集)

    题目链接  2017 ACM-ICPC Beijing Regional Contest Problem C 题意  给定一个$n$个点$m$条边的无向图.现在有$q$个询问,每次询问格式为$[l, ...

  2. [CSP-S模拟测试]:ants(回滚莫队)

    题目描述 然而贪玩的$dirty$又开始了他的第三个游戏. $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$.最开始,它们按某个顺序排成一列.现在$dirty$想要进 ...

  3. loj#6517. 「雅礼集训 2018 Day11」字符串(回滚莫队)

    传送门 模拟赛的时候纯暴力竟然骗了\(70\)分-- 首先对于一堆\(g\)怎么计算概率应该很好想,用总的区间数减去不合法的区间数就行了,简而言之对\(g\)排个序,每一段长为\(d\)的连续序列的区 ...

  4. LOJ.6504.[雅礼集训2018 Day5]Convex(回滚莫队)

    LOJ 莫队.发现只需要维护前驱后继就可以了. 但是加入一个点需要找到它当前的前驱后继,很麻烦还带个\(\log\). 但是如果只有删除某个点,只需要更新一下它的前驱后继即可. 用回滚莫队就好惹. 撤 ...

  5. BZOJ.4241.历史研究(回滚莫队 分块)

    题目链接 \(Description\) 长度为n的数列,m次询问,每次询问一段区间最大的 \(A_i*tm_i\) (重要度*出现次数) \(Solution\) 好像可以用莫队做,但是取max的操 ...

  6. 2018.09.26 bzoj5218: [Lydsy2017省队十连测]友好城市(回滚莫队)

    传送门 比较简单的一道回滚莫队吧. 每次询问用bitset优化kosaraju统计答案. 就是有点难调. 然后向dzyo学长学习了回滚莫队的一种简洁的实现方式,就是直接建立一个sqrt(m)∗sqrt ...

  7. 2018.08.14 bzoj4241: 历史研究(回滚莫队)

    传送们 简单的回滚莫队,调了半天发现排序的时候把m达成了n... 代码: #include<bits/stdc++.h> #define N 100005 #define ll long ...

  8. BZOJ4241:历史研究(回滚莫队)

    Description IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. ...

  9. LOJ#6504. 「雅礼集训 2018 Day5」Convex(回滚莫队)

    题面 传送门 题解 因为并不强制在线,我们可以考虑莫队 然而莫队的时候有个问题,删除很简单,除去它和前驱后继的贡献即可.但是插入的话却要找到前驱后继再插入,非常麻烦 那么我们把它变成只删除的回滚莫队就 ...

随机推荐

  1. JMeter - 生成随机数/随机字符串/随机变量/随机日期

    1. Random - 随机数 1.1 作用 1.2 声明 1.3 例子 2. __RandomDate - 随机日期 2.1 作用 2.2 声明参数 2.3 例子 3. RandomString - ...

  2. 如何提高访问 GitHub 的速度

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年5月11日. 因为一些特殊的原因,国内访问Github的速度确实比较慢.国内访问Github经常会出现连接不上.图片加载不出来.文件无 ...

  3. java中的方法重载(overload)

    什么时候方法重载:当两个方法的功能是相似的,可以考虑使用方法重载.若两个方法根本没有关系,无必要使用方法重载. 什么时候代码会发生方法重载:三个条件:1,在同一个类中.2,方法名相同.3,参数列表相同 ...

  4. .NET混合开发解决方案24 WebView2对比CefSharp的超强优势

    系列目录     [已更新最新开发文章,点击查看详细] WebView2控件应用详解系列博客 .NET桌面程序集成Web网页开发的十种解决方案 .NET混合开发解决方案1 WebView2简介 .NE ...

  5. Python Pygal 模块安装和使用你get了吗?

    Pygal 是另一个简单易用的数据图库,它以面向对象的方式来创建各种数据图,而且使用 Pygal 可以非常方便地生成各种格式的数据图,包括 PNG.SVG 等.使用 Pygal 也可以生成 XML e ...

  6. SpringCloudAlibaba分布式流量控制组件Sentinel实战与源码分析-中

    实战示例 控制台初体验 Sentinel的控制台启动后,控制台页面的内容数据都是空的,接下来我们来逐步操作演示结合控制台的使用,在上一节也已说明整合SpringCloud Alibaba第一步先加入s ...

  7. 『现学现忘』Git后悔药 — 27、版本回退介绍

    目录 1.什么版本回退 2.需要了解两个知识点 (1)HEAD是什么 (2)HEAD指针用法 3.git reflog命令介绍 1.什么版本回退 版本回退也可以叫回滚. 若修改过的文件,不仅添加到了暂 ...

  8. ArrayList集合概述和基本使用和ArrayList集合的常用方法和遍历

    什么是ArrayList类 java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素.此类提供一些方法来操作内部存储 的元素. ArrayList 中可不断添加元素,其大 ...

  9. 【python】下载中国大学MOOC的视频

    [python]下载中国大学MOOC的视频 脚本目标: 输入课程id和cookie下载整个课程的视频文件,方便复习时候看 网站的反爬机制分析: 分析数据包的目的:找到获取m3u8文件的路径 1. 从第 ...

  10. [RCTF2015]EasySQL-1|SQL注入

    1.打开之后只有登录和注册两个功能,界面如下: 2.随便注册一个账户并进行登录,(注册admin时显示该账户已存在,考虑到是不是要获取到admin账户),发现可以进行改密操作,结果如下: 3.抓取各个 ...