鲁班七号
题目链接:YBT2023寒假Day13 A
题目大意
给你一个 n 个点的无向图,一开始没有边。
给你一个数 m 和一些操作,操作有两点之间连一条给出边权的边,和给出 u,v,x,b,c,设 fi=x+bi,问又多少个 0<=i<c 满足图中有一条 u 到 v 的路径使得边权和与 fi 关于 m 同余。
路径可以不是简单路径。
思路
考虑看
u
,
v
u,v
u,v 之间路径边权和可以是
s
s
s 的条件。
那首先
u
,
v
u,v
u,v 所在的连通块里面所有边边权的
gcd
\gcd
gcd 为
g
g
g,一个显然的事情是
g
∣
s
g|s
g∣s,但这是必要,但不知道充不充分。
首先
u
u
u 到
v
v
v 的路径可以表示乘
g
x
gx
gx,假设这个路径经过了连通块里面所有的点,那任意一条边我们都可以通过(反复)横跳使得这条边被多走两次。
那我们设每条边的边权是
l
i
l_i
li,也就是我们要找一组正整数
x
i
x_i
xi 使得
∑
i
=
1
t
x
i
l
i
≡
g
(
m
o
d
m
)
\sum\limits_{i=1}^tx_il_i\equiv g\pmod m
i=1∑txili≡g(modm)(这样就可以组成所有要的位置)
那我们乘
1
/
g
1/g
1/g 就有
∑
i
=
1
t
x
i
l
i
g
≡
1
(
m
o
d
m
g
)
\sum\limits_{i=1}^tx_i\dfrac{l_i}{g}\equiv 1\pmod {\frac{m}{g}}
i=1∑txigli≡1(modgm)
那算上原本的要是
∑
i
=
1
t
2
y
i
l
i
g
≡
s
g
−
x
(
m
o
d
m
g
)
\sum\limits_{i=1}^t2y_i\dfrac{l_i}{g}\equiv \dfrac{s}{g}-x\pmod{\frac{m}{g}}
i=1∑t2yigli≡gs−x(modgm)
那
y
i
=
2
−
1
(
(
s
g
−
x
+
m
g
)
m
o
d
m
g
)
x
i
y_i=2^{-1}((\dfrac{s}{g}-x+\dfrac{m}{g})\bmod \dfrac{m}{g})x_i
yi=2−1((gs−x+gm)modgm)xi
那有解的情况就是
2
2
2 有逆元,那就是要
gcd
(
2
,
m
g
)
=
1
\gcd(2,\frac{m}{g})=1
gcd(2,gm)=1 即
m
g
\dfrac{m}{g}
gm 是奇数。
那我们继续考虑,如果
m
g
\dfrac{m}{g}
gm 是偶数会怎样。
那当这个时候,同余方程不一定有解,那我们就要寻找另外的加边权方式。
会发现给的可能会有环,那用走奇环(注意这个奇偶是除了
g
g
g 之后的)来加也是一个方法。
就是当
x
m
o
d
2
≠
s
g
m
o
d
2
x\bmod 2\neq \dfrac{s}{g} \bmod 2
xmod2=gsmod2 我们给它加上一个大小为
g
y
gy
gy 的奇环(
y
y
y 为奇数)
就有
(
x
+
y
)
m
o
d
2
=
s
g
m
o
d
2
(x+y)\bmod 2=\dfrac{s}{g}\bmod 2
(x+y)mod2=gsmod2
那这个时候
g
∣
s
g|s
g∣s 也是充分的。
那剩下的情况是
m
g
\dfrac{m}{g}
gm 是偶数且不存在
/
g
/g
/g 的奇环。
那从前面我们推的地方就知道,条件应该是
x
m
o
d
2
=
s
g
m
o
d
2
x\bmod 2=\dfrac{s}{g}\bmod 2
xmod2=gsmod2。
那么接下来我们看如何求,我们所需要维护的是连通块里所有边边权的
gcd
\gcd
gcd,以及是否存在奇环。
至于
gcd
\gcd
gcd,我们可以直接用并查集维护连通块,直接维护每个并查集的
gcd
\gcd
gcd 即可。
至于判断奇环,会发现我们只用看二进制下的
0
/
1
0/1
0/1,不过不能只看一位,因为会除
g
g
g,所以我们可以记录每一位的
0
/
1
0/1
0/1,那搞带权的并查集即可维护。
然后是如何处理查询。
那就是有多少个
0
⩽
i
<
c
0\leqslant i<c
0⩽i<c 使得
g
∣
(
x
+
b
i
)
g|(x+bi)
g∣(x+bi),或者
2
g
∣
(
x
+
b
i
)
2g|(x+bi)
2g∣(x+bi) 或者
(
x
+
b
i
)
m
o
d
2
g
=
g
(x+bi)\bmod 2g=g
(x+bi)mod2g=g。
这个就是一个同余方程,直接 exgcd 解,记得里面还有一个
x
+
b
i
x+bi
x+bi 也要化就是了。
代码
#include <bits/stdc++.h>
const int S = 1 << 20;
char frd[S], *ihead = frd + S;
const char *itail = ihead;
inline char nxtChar()
{
if (ihead == itail)
fread(frd, 1, S, stdin), ihead = frd;
return *ihead++;
}
template <class T>
inline void read(T &res)
{
char ch;
while (ch = nxtChar(), !isdigit(ch));
res = ch ^ 48;
while (ch = nxtChar(), isdigit(ch))
res = res * 10 + ch - 48;
}
char fwt[S], *ohead = fwt;
const char *otail = ohead + S;
inline void outChar(char ch)
{
if (ohead == otail)
fwrite(fwt, 1, S, stdout), ohead = fwt;
*ohead++ = ch;
}
template <class T>
inline void put(T x)
{
if (x > 9)
put(x / 10);
outChar(x % 10 + 48);
}
//void read(int &res) {
// scanf("%d", &res);
//}
//
//void outChar(char ch) {
// putchar(ch);
//}
//
//void put(int x) {
// printf("%d", x);
//}
浠ヤ笅涓虹ず渚?
//
//int main()
//{
// int a, b;
// read(a); read(b);
// put(a + b), outChar('\n');
//}
using namespace std;
const int N = 1e6 + 100;
int n, m, q, fa[N], val[N][21], g[N];
bool tg[N][21];
int gcd(int x, int y) {
if (!y) return x;
return gcd(y, x % y);
}
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1; y = 0;
return a;
}
int re = exgcd(b, a % b, y, x);
y -= a / b * x;
return re;
}
void down(int now) {
if (now == fa[now]) return ;
down(fa[now]);
for (int i = 0; i <= 20; i++) val[now][i] ^= val[fa[now]][i];
}
int Find(int now) {
if (fa[now] == now) return now;
return fa[now] = Find(fa[now]);
}
int find(int now) {
down(now); return Find(now);
}
void merge(int x, int y, int w) {
int X = find(x), Y = find(y); g[X] = gcd(g[X], w);
if (X == Y) {
for (int i = 0; i <= 20; i++)
if (val[x][i] ^ val[y][i] ^ ((w >> i) & 1)) tg[X][i] = 1;
}
else {
for (int i = 0; i <= 20; i++)
val[Y][i] = val[y][i] ^ ((w >> i) & 1) ^ val[x][i], tg[X][i] |= tg[Y][i];
g[X] = gcd(g[X], g[Y]);
fa[Y] = X;
}
}
int work(int a, int b, int m, int r) {
int d = gcd(a, m);
if (b % d) return 0;
int x, y; exgcd(a, m, x, y);
x = (x + m) % m;
long long X = 1ll * (b / d) * x; int bit = m / d;
X = X % bit;//出第一个偏差的位置
if (X > r) return 0;//出不到第一个
return 1 + (r - X) / bit;
}
int main() {
freopen("path.in", "r", stdin);
freopen("path.out", "w", stdout);
read(n); read(m); read(q);
for (int i = 1; i <= n; i++) fa[i] = i;
while (q--) {
int op; read(op);
if (op == 1) {
int u, v, w; read(u); read(v); read(w);
merge(u, v, w);
}
if (op == 2) {
int u, v, x, b, c; read(u); read(v); read(x); read(b); read(c);
if (find(u) != find(v)) {
put(0); outChar('\n');
continue;
}
int X = find(u), G = gcd(g[X], m);
if ((m / G) & 1) put(work(b % G, (G - x % G) % G, G, c - 1));
else {
int k = 0, tmp = G; while (!(tmp & 1)) tmp >>= 1, k++;
if (tg[X][k]) put(work(b % G, (G - x % G) % G, G, c - 1));
else {
if (val[u][k] ^ val[v][k]) put(work(b % (2 * G), (3 * G - x % (2 * G)) % (2 * G), 2 * G, c - 1));
else put(work(b % (2 * G), (2 * G - x % (2 * G)) % (2 * G), 2 * G, c - 1));
}
}
outChar('\n');
}
}
fwrite(fwt, 1, ohead - fwt, stdout);
return 0;
}