Codeforces 1500B/1501D

Codeforces Round #707 (Div. 1, based on Moscow Open Olympiad in Informatics) B. Two chandeliers

Codeforces Round #707 (Div. 2, based on Moscow Open Olympiad in Informatics) D. Two chandeliers


题意

给定两个循环节分别为(n,m)的数组({a})({b}),这两个数组可以无限循环延伸,在一个循环节中的数字互不相同

现在要从位置(1)开始走,({a})数组第(i)步的值为(a_{(i-1)\%n+1})({b})数组第(i)步的值为(a_{(i-1)\%m+1})

问一个最小的值(x),使得前(x)步中,(a_{(i-1)\%n+1} eq a_{(i-1)\%m+1})的步数至少为(k)

保证两数组不同(题目一定有解)

限制

(1le n,mle 5cdot 10^5)

(1le kle 10^{12})

(1le a_i,b_ile 2cdot max(n,m))




思路

由于(a_{(i-1)\%n+1} eq a_{(i-1)\%m+1})的步数函数是一个单调非递减的函数

故可以通过二分答案(x)确定这个最小值


因为在一个循环节中的数字互不相同

故对于任意一个可能在某一步相同的数而言,它出现的循环节长度应为(lcm(n,m))

而对于这个数第一次出现的位置(p),假设其在({a})数组中下标为(i),在({b})数组中下标为(j)

可以得到以下两式:

[left { egin{aligned} p\%n&=i\ p\%m&=j end{aligned} ight . ]

套扩展中国剩余定理即可(由于二分对下标不影响,故这部分需预处理)


其后每次二分check时,只需要枚举一次({a})数组,获取位置(i)的数字(a_i)首次出现的步数(p_i)

那么在前(x)步中,数字(a_i)相等的次数为(lfloor frac{x-p_i}{lcm} floor +1)

求出所有数字相等的次数总和(sum),那么不相等的次数即(x-sum)

检查是否(x-sumge k)即可




代码

(904ms/2000ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll LINF=0x3f3f3f3f3f3f3f3f;

int n,m,a[500050],b[500050],p[1000050];
ll u[5],v[5],x[500050],k,LCM;

ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(!b)
        d=a,x=1,y=0;
    else{
        ex_gcd(b,a%b,d,y,x);
        y-=x*(a/b);
    }
}
ll ex_crt(ll *m,ll *r,int n){
    ll M=m[1],R =r[1],x,y,d;
    for(int i=2;i<=n;i++){
        ex_gcd(M,m[i],d,x,y);
        if((r[i]-R)%d)
            return -1;
        x=(r[i]-R)/d*x%(m[i]/d);
        R+=x*M;
        M=M/d*m[i];
        R%=M;
    }
    return R>0?R:R+M;
}

bool check(ll step)
{
    ll sum=0;
    for(int i=1;i<=n;i++)
    {
        if(x[i]==-1||step<x[i])
            continue;
        sum+=(step-x[i])/LCM+1;
    }
    return step-sum>=k;
}

void solve()
{
    cin>>n>>m>>k;
    LCM=1LL*n*m/gcd(n,m);
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=m;i++)
        cin>>b[i],p[b[i]]=i;
    for(int i=1;i<=n;i++)
    {
        if(!p[a[i]]) //a中出现的数在b中未出现
        {
            x[i]=-1;
            continue;
        }
        u[1]=n,v[1]=i%n; //第i步下标为i%n,将下标n当作下标0即可,下同
        u[2]=m,v[2]=p[a[i]]%m;
        x[i]=ex_crt(u,v,2);
    }
    ll l=1,r=LINF;
    while(l<=r)
    {
        ll mid=l+r>>1;
        if(check(mid))
            r=mid-1;
        else
            l=mid+1;
    }
    cout<<l<<'
';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

https://blog.csdn.net/qq_36394234/article/details/115185159

原文地址:https://www.cnblogs.com/stelayuri/p/14580145.html