给你一个 n 个点的无向图,一开始没有边。
给你一个数 m 和一些操作,操作有两点之间连一条给出边权的边,和给出 u,v,x,b,c,设 fi=x+bi,问又多少个 0<=i
鲁班七号
题目大意
给你一个 n 个点的无向图,一开始没有边。
给你一个数 m 和一些操作,操作有两点之间连一条给出边权的边,和给出 u,v,x,b,c,设 fi=x+bi,问又多少个 0<=i<c 满足图中有一条 u 到 v 的路径使得边权和与 fi 关于 m 同余。
路径可以不是简单路径。
思路
考虑看 u , v u , v 之间路径边权和可以是 s s 的条件。
那首先 u , v u , v 所在的连通块里面所有边边权的 gcd gcd 为 g g ,一个显然的事情是 g | s g | s ,但这是必要,但不知道充不充分。
首先 u u 到 v v 的路径可以表示乘 g x g x ,假设这个路径经过了连通块里面所有的点,那任意一条边我们都可以通过(反复)横跳使得这条边被多走两次。
那我们设每条边的边权是 l i l i ,也就是我们要找一组正整数 x i x i 使得 t ∑ i = 1 x i l i ≡ g ( mod m ) ∑ i = 1 t x i l i ≡ g ( mod m ) (这样就可以组成所有要的位置)
那我们乘 1 / g 1 / g 就有 t ∑ i = 1 x i l i g ≡ 1 ( mod m g ) ∑ i = 1 t x i l i g ≡ 1 ( mod m g )
那算上原本的要是 t ∑ i = 1 2 y i l i g ≡ s g − x ( mod m g ) ∑ i = 1 t 2 y i l i g ≡ s g − x ( mod m g )
那 y i = 2 − 1 ( ( s g − x + m g ) mod m g ) x i y i = 2 − 1 ( ( s g − x + m g ) mod m g ) x i
那有解的情况就是 2 2 有逆元,那就是要 gcd ( 2 , m g ) = 1 gcd ( 2 , m g ) = 1 即 m g m g 是奇数。
那我们继续考虑,如果 m g m g 是偶数会怎样。
那当这个时候,同余方程不一定有解,那我们就要寻找另外的加边权方式。
会发现给的可能会有环,那用走奇环(注意这个奇偶是除了 g g 之后的)来加也是一个方法。
就是当 x mod 2 ≠ s g mod 2 x mod 2 ≠ s g mod 2 我们给它加上一个大小为 g y g y 的奇环(y y 为奇数)
就有 ( x + y ) mod 2 = s g mod 2 ( x + y ) mod 2 = s g mod 2
那这个时候 g | s g | s 也是充分的。
那剩下的情况是 m g m g 是偶数且不存在 / g / g 的奇环。
那从前面我们推的地方就知道,条件应该是 x mod 2 = s g mod 2 x mod 2 = s g mod 2 。
那么接下来我们看如何求,我们所需要维护的是连通块里所有边边权的 gcd gcd ,以及是否存在奇环。
至于 gcd gcd ,我们可以直接用并查集维护连通块,直接维护每个并查集的 gcd gcd 即可。
至于判断奇环,会发现我们只用看二进制下的 0 / 1 0 / 1 ,不过不能只看一位,因为会除 g g ,所以我们可以记录每一位的 0 / 1 0 / 1 ,那搞带权的并查集即可维护。
然后是如何处理查询。
那就是有多少个 0 ⩽ i < c 0 ⩽ i < c 使得 g | ( x + b i ) g | ( x + b i ) ,或者 2 g | ( x + b i ) 2 g | ( x + b i ) 或者 ( x + b i ) mod 2 g = g ( x + b i ) mod 2 g = g 。
这个就是一个同余方程,直接 exgcd 解,记得里面还有一个 x + b i x + b i 也要化就是了。
代码
#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 );
}
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 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2022-02-22 【luogu P6242】【模板】线段树 3(吉司机线段树)
2022-02-22 【NOI2021模拟测试赛(四十二)】D(cdq分治)(树状数组)
2022-02-22 【NOI2021模拟测试赛(四十二)】chemistry(珂朵莉树)(动态开点线段树)
2021-02-22 【luogu P3690】【模板】Link Cut Tree (动态树)
2021-02-22 【luogu P3384】【模板】轻重链剖分
2021-02-22 【ybt金牌导航5-1-1】【luogu P2590】树的统计
2021-02-22 【ybt金牌导航4-5-1】【luogu P3369】普通平衡树(替罪羊树做法)