Address
Algorithm 1
設 表示是否存在終點在點 且權值和模 的值爲 的路徑,對每組詢問記憶化搜索即可,時間複雜度 ,期望得分 。
Algorithm 2
針對 的數據。
詢問時 到 之間的任意一條路徑可以看做是 到 之間的某一條路徑再加上連通塊中若干個環的邊權和的和,所以可以用並查集維護出每個點到並查集根的任意一條路徑的邊權和,一旦所在連通塊中存在邊權和爲 的環,就可以構造出任意權值的路徑,否則 之間的路徑和可以看做是到並查集根的路徑和。
時間複雜度 ,結合算法一期望得分 。
Algorithm 3
針對 是質數的數據。
由同餘的理論可知:
的情況用算法二處理。
對於 的情況下, 詢問時若 所在連通塊中存在邊權 模 的值不爲 的邊,因爲 ,由上述理論可知,我們只要在走到這條邊上之後在這條邊上不停地繞圈,就可以構造出任意權值的路徑,因此在用並查集維護時額外記錄連通塊是否存在邊權模 的值不爲 的邊即可。
時間複雜度 ,結合前述算法期望得分 。
Algorithm 4
針對 是奇數的數據。
同樣地,詢問時對於 所在連通塊中邊權爲 的邊,因爲 ,設連通塊中的邊集爲 ,則 間所有路徑的邊權和組成的集合爲:
令 ,因爲 是一個等差數列,對於合法的 可以列出一個同餘方程:
用擴展歐幾里得算法解之即可,若有解,求出最小正整數解 ,則解集爲 ,不難算出 內的解數。
同樣用並查集維護連通塊內所有邊權和 的 即可。
時間複雜度 ,結合前述算法期望得分 。
Algorithm 5
考慮 沒有任何限制的情況。
類似算法二,我們可以把 之間的任意一條路徑表示成 之間的某一條路徑加上 和連通塊中所有環邊權和的 的若干倍。
在路徑中加入一個環可以通過以下方式構造:在從點 到這個環的路徑上來回走 遍,在中間的某一次加入這個環。
我們只需要在加入一條邊時,加入繞這條邊一圈的邊權和以及這條邊和原來的那些邊新形成的某一個環的邊權和,就可以維護出所有環邊權和的 。
考慮這樣做的正確性,我們只需要討論將兩個環合併的合法性,合併時只有以下兩種情況:
-
若兩個環只有一個交點,設兩環的邊權和爲 ,顯然有 ;
-
若兩個環的交是一條鏈,且鏈的邊權和爲 ,設兩環的邊權和爲 ,我們只需要證明:
由輾轉相減法,可得:
顯然最後劃得的結果整除 ,原命題得證。
同算法四對 列出同餘方程解之即可。
時間複雜度 ,期望得分 。
Code
#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);
}
typedef long long ll;
const int N = 1e6 + 5;
int sze[N], fa[N], d[N], g[N];
int n, m, q;
inline ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
return x = 1, y = 0, a;
ll e = exgcd(b, a % b, y, x);
y -= a / b * x;
return e;
}
inline void add(int &x, int y)
{
x += y;
x >= m ? x -= m : 0;
}
inline void dec(int &x, int y)
{
x -= y;
x < 0 ? x += m : 0;
}
inline int ufs_find(int x)
{
if (fa[x] != x)
{
int t = ufs_find(fa[x]);
add(d[x], d[fa[x]]);
fa[x] = t;
return t;
}
return x;
}
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)
{
sze[i] = 1;
fa[i] = i;
d[i] = 0;
g[i] = m;
}
int opt, u, v, x, b, c;
while (q--)
{
read(opt);
read(u); read(v); read(x);
int tu = ufs_find(u),
tv = ufs_find(v);
if (opt == 1)
{
if (tu == tv)
{
int w1 = d[u], w2 = x;
add(w1, d[v]);
add(w1, x);
add(w2, x);
g[tu] = std::__gcd(g[tu], std::__gcd(w1, w2));
}
else
{
if (sze[tu] > sze[tv])
std::swap(tu, tv), std::swap(u, v);
sze[tv] += sze[tu];
fa[tu] = tv;
d[tu] = d[u];
add(d[tu], d[v]);
add(d[tu], x);
int w = x;
add(w, x);
g[tv] = std::__gcd(w, std::__gcd(g[tv], g[tu]));
}
}
else
{
read(b); read(c);
if (tu != tv)
outChar('0');
else
{
int w = d[u];
add(w, d[v]);
dec(w, x);
w %= g[tu];
ll x, y, e = exgcd(b, g[tu], x, y);
if (w % e != 0)
outChar('0');
else
{
ll tmp = g[tu] / e;
x = x % tmp;
x < 0 ? x += tmp : 0;
x = x * w / e % tmp;
if (x >= c)
outChar('0');
else
put((c - 1 - x) / tmp + 1);
}
}
outChar('\n');
}
}
fwrite(fwt, 1, ohead - fwt, stdout);
fclose(stdin); fclose(stdout);
return 0;
}