Live2D

[AGC031F] Walk on Graph

link

Solution

非常厉害的题捏,可惜我什么都想不到/kk

我们首先转化一下,我们对于 st 计算这个长度变为 ts 每次加入一个 w,当前权值 x 就变为 2x+w。这样就不需要在乎长度了。

所以我们可以考虑暴力设计状态 (u,x) 表示到了点 u 当前长度为 x,那么我们的暴力的做法就是对于这个状态暴力并查集,最后对于 st 要求长度为 r 的判断判断就是是否 (t,0)(s,r) 连通即可。显然无法通过,我们考虑仔细观察性质。


如果 (u,x)(v,y),那么我们同样存在 (v,y)(u,x)

假设我们是 uv 间存在一个真实长度为 l,值为 w 的边,那么我们存在 y=x2l+w,那么我们考虑来回走这条路径 t 次,那么值即是 x2tl+w(2tl12l1),这样的话因为模数不是质数似乎没法考虑,但是考虑到我们的路径都是长度为 1 的边构成的,所以我们不妨考虑 l=1,可以发现 t=2φ(p) 的时候我们是满足条件的,所以我们可以证明该性质。

为了方便,我们下面用 (u,x)(v,y) 来表示这种关系。


如果图中存在两条边权值分别为 a,b,那么存在 (u,x)(u,x+(ab)×3)

首先我们先考虑 u 连出两条权值为 a,b 的情况,我们可以发现如下关系式:

(u,4x+3a)(u,x)(u,4x+3b)

因为 4modp 的情况下是存在逆元的,所以 4x 其实在模 p 条件下可以表达任意值,所以我们可以推出:(u,x)(u,(ab)×3)

我们考虑 uv 存在连边的情况,假设 u,v 连边权值为 w,那么我们走 t 次的话就存在:(u,x)(v,x2t+w(2t1)) ,取 t=φ(p),可以发现 (u,x)(v,x)

有了以上两个条件,我们发现我们不断拓展就可以得到该性质。


有了上面的两个性质以及裴蜀定理,我们其实可以发现(wi 是第 i 条边的边权(长度)),如果设 g=gcd(wiwj),ij,那么 (u,x)(u,y) 当且仅当 xy(modgcd(3g,p)),所以我们完全可以把 pgcd(3g,p)。那么可以发现每一个 wi 都可以表示为 tg+z,tZ 的形式。

我们发现,如果把 wi 都减去 z,那么可以满足都是 g 的倍数,同时如果我们设 (u,x+z)=(u,x),那么对于一条边 w,我们可以发现:(u,x+z)(v,2(x+z)+wz)=(v,2x+w+z),所以就跟原来的转移相同了。

同时我们观察最后的查询形式,发现现在就是查询 (t,z)(s,r+z) 是否连通了。我们观察我们的转移,发现是 x2x+w 的形式,而我们现在 w 都是 g 的倍数,而我们的 p 至多是 3g,所以我们可以发现我们所有需要的 x 都可以表示为 z22r+i+jg,iZ,j{0,1,2}。而我们对于 uv 的权值为 w 的边,我们可以发现:(u,x)(u,4x+3w)(u,4x),也就是 2 的指数奇偶性相同时是可达的。所以其实我们只需要记录 z2i+jg 这种形式。所以只有 Θ(6n) 个状态。所以这个时候我们就可以暴力跑并查集了,然后判一下就好了。


复杂度 Θ(nlogn),瓶颈在于并查集。

Code

Copy
#include <bits/stdc++.h> using namespace std; #define Int register int #define MAXN 1000005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;} template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);} template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} template <typename T> inline void chkmax (T &a,T b){a = max (a,b);} template <typename T> inline void chkmin (T &a,T b){a = min (a,b);} int n,m,T,mod,tot,fa[MAXN * 6],ind[MAXN][2][3],vis[2][MAXN];//ind[u][0/1][0/1/2]表示u点2^{0/1}*x+g*(0/1/2) int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);} void unionSet (int u,int v){fa[findSet (u)] = findSet (v);} bool checkit (int u,int v){return findSet (u) == findSet (v);} struct edge{ int u,v,w; bool operator < (const edge &p)const{return w < p.w;} }seq[MAXN]; signed main(){ read (n,m,T,mod); for (Int u = 1;u <= n;++ u) for (Int p = 0;p < 2;++ p) for (Int q = 0;q < 3;++ q) ind[u][p][q] = ++ tot; for (Int u = 1;u <= tot;++ u) fa[u] = u; for (Int i = 1;i <= m;++ i) read (seq[i].u,seq[i].v,seq[i].w); sort (seq + 1,seq + m + 1);int g = mod,z; for (Int i = 2;i <= m;++ i) g = __gcd (g,seq[i].w - seq[i - 1].w); z = seq[1].w % g;for (Int i = 1;i <= m;++ i) seq[i].w = (seq[i].w - z) / g,seq[i].w %= 3; mod = __gcd (3 * g,mod); for (Int i = 1;i <= m;++ i){ int u = seq[i].u,v = seq[i].v,w = seq[i].w; for (Int p = 0;p < 2;++ p) for (Int q = 0;q < 3;++ q){ unionSet (ind[u][p][q],ind[v][p ^ 1][(q * 2 + w) % 3]); unionSet (ind[v][p][q],ind[u][p ^ 1][(q * 2 + w) % 3]); } } for (Int i = 0,now = z;i < mod;++ i,now = now * 2 % mod) vis[i & 1][now] = 1; for (Int i = 1;i <= T;++ i){ int u,v,r;read (u,v,r);swap (u,v); for (Int p = 0;p < 2;++ p) for (Int q = 0;q < 3;++ q) if (checkit (ind[u][0][0],ind[v][p][q]) && vis[p][(r + z - g * q % mod + mod) % mod]){ puts ("YES"); goto there; } puts ("NO"); there:; } return 0; }
posted @   Dark_Romance  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2021-10-20 [CSP-S2020] 函数调用 & 贪吃蛇
点击右上角即可分享
微信分享提示