[CF1515G]Phoenix and Odometers

Phoenix and Odometers

题解

由于他需要求的是 v v v的回路,所以我们可以先想到将原图进行缩点,对于每个连通块单独进行处理。
对于每个连通块内部,它的所有环都是可以被重复走很多遍的。
设它所有环的长度为 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak那么我最后走过的总长度可以表示为 p 1 a 1 + p 2 a 2 + . . . + p k a k p_1a_1+p_2a_2+...+p_ka_k p1a1+p2a2+...+pkak
在模 t t t的意义下,所有是 ( a 1 , a 2 , a 3 , . . . , a k , t ) (a_1,a_2,a_3,...,a_k,t) (a1,a2,a3,...,ak,t)的倍数的数都可以被表示出来。
所以我们只需要求出 ( a 1 , a 2 , a 3 , . . . , a k , t ) (a_1,a_2,a_3,...,a_k,t) (a1,a2,a3,...,ak,t)是不是 s s s的因数,如果是的话, t − s t-s ts也可以被走出来,只需要走 t − 1 t-1 t1遍长度为 s s s的路径即可。

但又要如何求出所有环的gcd呢?
我们可以先将dfs树建出来,只统计上面含一条非树边的环的长度的gcd
如果有含多条非树边的环,它必然可以由几个只含一条非树边的环组成,不会对答案造成影响。
而对于非树边 u − > v u->v u>v,它的环长度就是 d e p u − d e p v + w u − > v dep_u-dep_v+w_{u->v} depudepv+wu>v
再对于每个询问单独计算即可。

时间复杂度 O ( m l o g   n ) O\left(mlog\,n\right) O(mlogn)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define lowbit(x) (x&-x)
#define reg register
#define mp make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
const int INF=0x7f7f7f7f;
const int jzm=233;
const int mo=998244353;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,q,head[MAXN],tot,belong[MAXN],cnt,sta[MAXN],stak;
int dfn[MAXN],low[MAXN],idx;LL dis[MAXN],g[MAXN];
bool insta[MAXN],vis[MAXN];
struct edge{int to,nxt,paid;}e[MAXN];
void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
void tarjan(int u){
	dfn[u]=low[u]=++idx;sta[++stak]=u;insta[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v])tarjan(v),low[u]=min(low[u],low[v]);
		else if(insta[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){int v;++cnt;do{v=sta[stak--];belong[v]=cnt;insta[v]=0;}while(v!=u);}
}
void dosaka(int u){
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(belong[v]^belong[u])continue;
		if(!vis[v])dis[v]=dis[u]+1ll*e[i].paid,dosaka(v);
		else{
			LL tmp=dis[u]-dis[v]+1ll*e[i].paid;
			g[belong[v]]=!g[belong[v]]?tmp:gcd(tmp,g[belong[v]]);
		}
	}
}
int main(){
	read(n);read(m);
	for(int i=1,u,v,w;i<=m;i++)read(u),read(v),read(w),addEdge(u,v,w);
	for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
	for(int i=1;i<=n;i++)if(!vis[i])dosaka(i);
	read(q);
	for(int i=1;i<=q;i++){
		int v,s,t;read(v);read(s);read(t);
		if(!belong[v]||1ll*s%gcd(g[belong[v]],1ll*t))puts("NO");
		else puts("YES");
	}
	return 0;
}
 

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值