若两点间的路径上出现了环,则该环可以贡献给答案,因为可以从起点到环上一点来回跑 (m) 次,然后再跑环的贡献。
因为每个环都可以起任意倍数的贡献,所以由裴蜀定理得,所有环的总贡献为所有环的权值和的 (gcd) 的任意倍数,图是无向图,因此每条边都可以看作是一个环。
可以用带权并查集维护出每个连通块中所有环的 (gcd) 和每个点到该并查集的根的距离,维护距离是为了知道两点间某条路径的权值和,因为每条边都加入到了环的贡献,所以该路径可以任选。
考虑如何解决 (x+kb,k in [0,c-1]) 中权值出现个数。设环的贡献为 (g),两点的某条路径的权值和为 (dis),得:
[large x+kb equiv dis pmod{g}
]
然后可以用扩展欧几里得算法求得最小的 (k),每隔 (frac{g}{gcd(b,g)}) 就出现一个合法权值。
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,q;
int fa[maxn],g[maxn];
ll d[maxn];
int find(int x)
{
if(x==fa[x]) return x;
int anc=find(fa[x]);
d[x]=(d[x]+d[fa[x]])%m;
return fa[x]=anc;
}
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
void exgcd(int a,int b,int &x,int &y)
{
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
int main()
{
read(n),read(m),read(q);
for(int i=1;i<=n;++i) fa[i]=i,g[i]=m;
while(q--)
{
int opt,x,y,fx,fy,w,b,c,t,A,B,C,X,Y;
read(opt),read(x),read(y),read(w),fx=find(x),fy=find(y);
if(opt==1)
{
if(fx==fy) g[fx]=gcd(g[fx],gcd((d[x]+d[y]+w)%m,2*w));
else fa[fx]=fy,d[fx]=(d[x]+d[y]+w)%m,g[fy]=gcd(gcd(g[fx],g[fy]),2*w);
}
else
{
read(b),read(c),A=b%g[fx],B=g[fx],C=(d[x]+d[y]-w+B)%B,t=gcd(A,B);
if(fx!=fy||C%t)
{
puts("0");
continue;
}
A/=t,B/=t,C/=t,exgcd(A,B,X,Y),X=((ll)X*C%B+B)%B;
printf("%lld
",X<c?(c-1-X)/B+1:0);
}
}
return 0;
}