AtCoder AGC031F Walk on Graph (图论、数论)
題目鏈接
https://atcoder.jp/contests/agc031/tasks/agc031_f
題解
這題真是太神仙了……
首先我們轉(zhuǎn)化一下問(wèn)題,倒著來(lái)做,一開(kāi)始有一個(gè)數(shù)\(0\), 每次走過(guò)一條邊該數(shù)變?yōu)槌艘?span id="ozvdkddzhkzd" class="math inline">\(2\)再加上這條邊的邊權(quán)。
我們用\((u,x)\)代表一個(gè)狀態(tài),表示當(dāng)前在點(diǎn)\(u\),該數(shù)值為\(x\), \(x\)始終在\(\mod p\)意義下定義,\(p\)為模數(shù)。
假設(shè)\(u\)點(diǎn)有一條邊連向\(v\)邊權(quán)為\(w\), 則\((u,x)\)可以變成\((v,2x-w)\)(下用\(\Rightarrow\)表示),然后\((v,2x-w)\Rightarrow(u,4x-3w),(u,4x-3w)\Rightarrow(v,8x-7w),...\)
但是由于\(p\)是奇數(shù),存在\(k>1\)使\(2^k\equiv 1(\mod p)\), 這個(gè)序列是循環(huán)的,由\((u,x)\)最終依然會(huì)到達(dá)\((u,x)\). 也就是說(shuō),我們可以認(rèn)為這個(gè)遞推關(guān)系是雙向的,\((u,x)\Leftrightarrow (v,2x-w)\Leftrightarrow (u,4x-3w)\Leftrightarrow ...\)
這時(shí),不要往\(2\)的冪的方向思考而一去不復(fù)返。考慮這個(gè)遞推序列的前三步: \((u,x)\Leftrightarrow (u,4x-3w)\)
如果有兩條連接的邊分別是\(w_1,w_2\), 則\((u,x)\Leftrightarrow (u,4x-3w_1) \Leftrightarrow (u,4x-3w_2)\). 又因?yàn)?span id="ozvdkddzhkzd" class="math inline">\(4\)在\(\mod p\)意義下有逆元,故\(4x\)可以代表任何數(shù),即\((u,x)\Leftrightarrow (u,x\pm 3(w_1-w_2))\).
考慮一個(gè)點(diǎn)相連的那些邊,由數(shù)論的基本知識(shí)可得,\((u,x)\Leftrightarrow (u,x+kt_u)\) (\(k\)為整數(shù)),其中\(t_u=\gcd(3g_u,p)\), \(g_u\)為與\(u\)相連所有邊權(quán)的\(\gcd\). 即所有模\(t_u=\gcd(3g_u,p)\)同余的\(x\), \((u,x)\)的狀態(tài)是一樣的。
再進(jìn)一步說(shuō),考慮不同的點(diǎn),\((u,x)\Leftrightarrow (v,2x+w)\), \(u\)循環(huán)節(jié)為\(t_u\)導(dǎo)致\(v\)具有長(zhǎng)度為\(2t_u\)的循環(huán)節(jié),和其本身的取\(\gcd\)得\(\gcd(t_u,2t_v)=\gcd(t_u,t_v)\) (\(2\)可以去掉是因?yàn)?span id="ozvdkddzhkzd" class="math inline">\(2\)與\(3g_u\)互質(zhì)因而與\(t_u\)互質(zhì))。由于整個(gè)圖是聯(lián)通的,這可以擴(kuò)展到所有點(diǎn),也就是對(duì)于所有\(u,x\)有\((u,x)\Leftrightarrow (u,x+T)\), 其中\(T=\gcd^n_{u=1}t_u=\gcd(3\gcd^m_{i=1}w_i,p)\). 我們令\(G=\gcd^m_{i=1}w_i\). 顯然\(T=G\)或\(T=3G\).
整理一下,同一點(diǎn)所有模\(T\)同余的狀態(tài)是一樣的,而所有邊權(quán)是模\(G\)同余的,且\(T=G\)或\(T=3G\).
這樣,我們可以把題目中的\(p\)直接改成\(T\), 不會(huì)對(duì)答案有任何影響。下面我們用\(p\)來(lái)代表\(T\).
注: 這里之所以我們只取了\((4x-3y)\)這一項(xiàng)而沒(méi)有繼續(xù)深入下去依然能得到正確的結(jié)論的原因是對(duì)任何\(k\in \mathbb{N}\), \((u,2^{2k}x+(2^{2k}-1)w)\), 而\(3|(2^{2k}-1)\).
既然所有邊權(quán)是模\(G\)同余的,我們就可以設(shè)\(w_i=c_iG+z\), 其中\(0\le z\lt G\)是一個(gè)對(duì)于所有的邊\(i\)都一樣的數(shù)。
這個(gè)邊權(quán)帶常數(shù)\(z\)看上去很不優(yōu)美,我們想辦法把它消掉!我們把所有的當(dāng)前數(shù)值\(x\)都增加\(z\), 并把所有的邊權(quán)都減少\(z\). 改變后的值記為\(x'\), 那么現(xiàn)在的一次游走將會(huì)是: \(x'-z=x\rightarrow (2x+c_iG+z)=(2(x+z)+c_iG)-z=(2x'+c_iG)-z\), 也就是說(shuō)\(x'\)經(jīng)過(guò)一次操作會(huì)變成\(2x'+c_iG\), \(z\)的余數(shù)沒(méi)了,其余操作沒(méi)有變化!
于是我們執(zhí)行把數(shù)值\(x\)增加\(z\)同時(shí)把邊權(quán)減少\(z\)的操作,現(xiàn)在我們認(rèn)為\(w_i=c_iG\),且詢問(wèn)的是\((t,z)\)與\((s,z+r)\)是否可達(dá)。
因?yàn)?span id="ozvdkddzhkzd" class="math inline">\(w_i=c_iG\),故\((u,x)\Leftrightarrow (u,4x+3c_iG)\Leftrightarrow (u,4x)\), 且因?yàn)檫厵?quán)全部是\(G\)的倍數(shù),在\(\mod 3G\)意義下只有\(3\)種取值,即可以認(rèn)為\(c_i=0,1,2\). 那每個(gè)狀態(tài)就可以表示成\((u,2^jx+kG)\)的形式,其中\(0\le j\lt 2,0\le k\lt 3\).
也就是說(shuō),對(duì)于每個(gè)點(diǎn),我們只有\(6\)種狀態(tài)有用!
然后的做法就很顯然了,我們建出這\(6n\)個(gè)狀態(tài),對(duì)于每一條輸入的邊,操作權(quán)值之后把相應(yīng)的狀態(tài)用并查集縮起來(lái)。
對(duì)于詢問(wèn),我們想要的是\((t,z)\)和\((s,z+r)\)是否聯(lián)通。枚舉\(j\mod 2\)與\(k\), 我們得到\(2^jz+kG\equiv z+r(\mod p)\). 我們需要判斷是否存在\(j\)滿足\(2^jz\equiv z+r-kG(\mod p)\), 這個(gè)對(duì)于每個(gè)\(j\)預(yù)處理一下\(2^jz\)記錄下來(lái)奇偶即可。若存在且\((t,0,0)\)和\((s,j,k)\)這兩個(gè)狀態(tài)聯(lián)通,則答案是YES, 否則是NO.
時(shí)間復(fù)雜度\(O(6(n+m+q)\alpha(n)+p)\).
代碼
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair #define riterator reverse_iterator using namespace std;inline int read() {int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f; }const int N = 5e4; const int mxP = 1e6; struct AEdge {int u,v,w; } ae[N+3]; int uf[N*6+3]; bool ispw2[mxP+3][2]; int n,m,q,p,g;int gcd(int x,int y) {return y==0?x:gcd(y,x%y);}int getid(int x,int j,int k) {return (j*3+k)*n+x;} int findfa(int u) {return u==uf[u]?u:uf[u]=findfa(uf[u]);} void unionfa(int u,int v) {int x = findfa(u),y = findfa(v);if(x!=y) {uf[x] = y;} }int main() {scanf("%d%d%d%d",&n,&m,&q,&p); g = p;for(int i=1; i<=n*6; i++) uf[i] = i;for(int i=1; i<=m; i++){scanf("%d%d%d",&ae[i].u,&ae[i].v,&ae[i].w);g = gcd(g,abs(ae[i].w-ae[1].w));}p = gcd(p,3*g);int z = ae[1].w%g;for(int i=1; i<=m; i++){int w = (ae[i].w-z)/g%3,u = ae[i].u,v = ae[i].v;for(int j=0; j<2; j++) for(int k=0; k<3; k++){unionfa(getid(u,j,k),getid(v,j^1,(k+k+w)%3));unionfa(getid(v,j,k),getid(u,j^1,(k+k+w)%3));}}for(int i=0,x=z; i<p; i++,x=(x<<1)%p) ispw2[x][i&1] = true;while(q--){int u,v,l; scanf("%d%d%d",&u,&v,&l); bool ans = false;for(int j=0; j<2; j++) for(int k=0; k<3; k++){int tmp = (z+l+(3-k)*g)%p;if(ispw2[tmp][j]){int uu = findfa(getid(v,0,0)),vv = findfa(getid(u,j,k));if(uu==vv) {ans = true; break;}}}if(ans==true) puts("YES"); else puts("NO");}return 0; } 與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的AtCoder AGC031F Walk on Graph (图论、数论)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AtCoder AGC031E Snuk
- 下一篇: AtCoder AGC033F Addi