[数论] LOJ #508. 「LibreOJ NOI Round #1」失控的未来交通工具

这种非简单路 一般转化成任意一条路加上若干环
这里大概是任意一条路加上若干环长的gcd
任意一条路 可以弄出任意一颗生成树? 但是这里实际上只需要一个带权并查集
详见官方题解

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=1000005;

inline abcd GCD(ll a,ll b){
  if (!b) return abcd(1,0);
  abcd t=GCD(b,a%b);
  return abcd(t.second,t.first-a/b*t.second);
}
inline ll calc(ll a,ll b,ll n){
  abcd G=GCD(a,n); ll d=a*G.first+n*G.second;
  if (b%d) return -1;
  return (G.first*(b/d)%(n/d)+(n/d))%(n/d);
}

int n,m,Q;

int fat[N]; int g[N],dis[N];
inline int Fat(int u){
  if (u==fat[u]) return u;
  int f=fat[u];
  fat[u]=Fat(fat[u]);
  (dis[u]+=dis[f])%=m;
  return fat[u];
}
inline void Merge(int x,int y,int w){
  int fx=Fat(x),fy=Fat(y);
  if (fx==fy){
    int l=(dis[x]+dis[y])%m;
    g[fx]=__gcd(__gcd(g[fx],2*w),w+l);
  }else{
    fat[fx]=fy; dis[fx]=((ll)w+m-dis[x]+m-dis[y])%m;
    g[fy]=__gcd(__gcd(g[fy],g[fx]),2*dis[fx]);
  }
}

int main(){
  int order,u,v,x,b,c,w;
  read(n); read(m); read(Q);
  for (int i=1;i<=n;i++) fat[i]=i;
  while (Q--){
    read(order); read(u); read(v);
    if (order==1){
      read(w); Merge(u,v,w);
    }else{
      read(x); read(b); read(c);
      ll ans=0;
      if (Fat(u)==Fat(v)){
    int L=(dis[u]+dis[v])%m;
    int g=__gcd(::g[Fat(u)],m);
    int k=calc(b%g,((L-x)%g+g)%g,g);
    if (k!=-1 && k<c){
      int t=g/__gcd(g,b);
      ans=(c-1-k)/t+1;
    }
      }
      printf("%lld\n",ans);
    }
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值