CF1515G

CF1515G Phoenix and Odometers

首先进行缩点,对于一个点,与它不在同一连通分量的点无法形成回路,所以对每个连通分量分别计算。

假设一个点 u,有两个长度为 ab 的环,那么就相当于找两个非负整数 xy,使得 ax+by=w,其中 w 为题中的路径长,根据裴蜀定理得到上述方程成立当且仅当 gcd(a,b)w

考虑如何求出 gcd(a,b),即点 u 所有环长的 gcd。

首先在强连通分量中存在四种边:树边、横叉边、返祖边、前向边。

举个例子:

image

假设根节点为 1,根节点通过树边到节点 u 的路径长度为 disu

以横叉边 53 这条边为例,设它的边权为 w。因为这些点在同一连通分量且它是横叉边,说明 3 这边已经可以形成回到 1 的一条回路了。

所以其中一条回路为 1 通过树边连向 33 再通过一些非树边连回 1,大体路径为 131,长度可以表示为 dis3+val(3,1)

又因为当前的横叉边 53,产生了一条新的回路。

1 通过树边连向 55 通过横叉边连向 33 通过一些非树边连回 1,大体路径为 1531,长度为 dis5+w+val(3,1)

这两条回路的 gcd,可以使用辗转相减进行化简,将公共路径部分减掉,则 gcd 为 gcd(dis3+val(3,1),dis5+wdis3)

这样对于一条横叉边 uv,对 gcd 产生的贡献为 disu+wdisv

然后通过分析发现,返祖边和前向边对 gcd 的贡献也是 disu+wdisv,即所有的非树边,对答案的贡献是相同的。

求出所有环长的 gcd,设为 g,然后根据题意得出 a×g+s=b×t,再一次使用裴蜀定理得到 gcd(g,t)s

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,q;
const int maxn=2e5+10;
struct node{
    int to,nxt,w;
};
node e[maxn];
int head[maxn],tot;
void add(int u,int v,int w){
    ++tot;
    e[tot].to=v;
    e[tot].nxt=head[u];
    e[tot].w=w;
    head[u]=tot;
}
int dfn[maxn],low[maxn],st[maxn];
bool vis[maxn];
int scc[maxn];
int res,tp,cnt;
void tarjan(int u){
    dfn[u]=low[u]=++res;
    st[++tp]=u;
    vis[u]=1;
    for(int i=head[u];i!=0;i=e[i].nxt){
        int v=e[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(dfn[u]==low[u]){
        ++cnt;
        while(st[tp]!=u){
            scc[st[tp]]=cnt;
            vis[st[tp]]=0;
            --tp;
        }
        scc[st[tp]]=cnt;
        vis[st[tp]]=0;
        --tp;
    }
}
int dis[maxn],g[maxn];
bool tag[maxn];
void dfs(int u,int cur){
    tag[u]=1;
    for(int i=head[u];i!=0;i=e[i].nxt){
        int v=e[i].to;
        int w=e[i].w;
        if(scc[v]!=cur){
            continue;
        }
        if(tag[v]){
            g[cur]=__gcd(g[cur],dis[u]-dis[v]+w);
            continue;
        }
        dis[v]=dis[u]+w;
        dfs(v,cur);
    }
}
signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
    }
    for(int i=1;i<=n;i++){
        if(!dfn[i]){
            tarjan(i);
        }
    }
    for(int i=1;i<=n;i++){
        if(!tag[i]){
            dfs(i,scc[i]);
        }
    }
    scanf("%lld",&q);
    while(q--){
        int u,s,t;
        scanf("%lld%lld%lld",&u,&s,&t);
        if(s%__gcd(g[scc[u]],t)==0){
            printf("YES\n");
        }
        else{
            printf("NO\n");
        }
    }
    return 0;
}
posted @   JIA-Shells  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示