[AGC031F] Walk on Graph
Solution
非常厉害的题捏,可惜我什么都想不到/kk
我们首先转化一下,我们对于 计算这个长度变为 每次加入一个 ,当前权值 就变为 。这样就不需要在乎长度了。
所以我们可以考虑暴力设计状态 表示到了点 当前长度为 ,那么我们的暴力的做法就是对于这个状态暴力并查集,最后对于 要求长度为 的判断判断就是是否 与 连通即可。显然无法通过,我们考虑仔细观察性质。
如果 ,那么我们同样存在 。
假设我们是 间存在一个真实长度为 ,值为 的边,那么我们存在 ,那么我们考虑来回走这条路径 次,那么值即是 ,这样的话因为模数不是质数似乎没法考虑,但是考虑到我们的路径都是长度为 的边构成的,所以我们不妨考虑 ,可以发现 的时候我们是满足条件的,所以我们可以证明该性质。
为了方便,我们下面用 来表示这种关系。
如果图中存在两条边权值分别为 ,那么存在 。
首先我们先考虑 连出两条权值为 的情况,我们可以发现如下关系式:
因为 在 的情况下是存在逆元的,所以 其实在模 条件下可以表达任意值,所以我们可以推出:。
我们考虑 与 存在连边的情况,假设 连边权值为 ,那么我们走 次的话就存在: ,取 ,可以发现 。
有了以上两个条件,我们发现我们不断拓展就可以得到该性质。
有了上面的两个性质以及裴蜀定理,我们其实可以发现( 是第 条边的边权(长度)),如果设 ,那么 当且仅当 ,所以我们完全可以把 。那么可以发现每一个 都可以表示为 的形式。
我们发现,如果把 都减去 ,那么可以满足都是 的倍数,同时如果我们设 ,那么对于一条边 ,我们可以发现:,所以就跟原来的转移相同了。
同时我们观察最后的查询形式,发现现在就是查询 与 是否连通了。我们观察我们的转移,发现是 的形式,而我们现在 都是 的倍数,而我们的 至多是 ,所以我们可以发现我们所有需要的 都可以表示为 。而我们对于 的权值为 的边,我们可以发现:,也就是 的指数奇偶性相同时是可达的。所以其实我们只需要记录 这种形式。所以只有 个状态。所以这个时候我们就可以暴力跑并查集了,然后判一下就好了。
复杂度 ,瓶颈在于并查集。
Code
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2021-10-20 [CSP-S2020] 函数调用 & 贪吃蛇