Codeforces 1500B/1501D - Two chandeliers (扩展中国剩余定理、二分)

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}\neq a_{(i-1)\%m+1}\)的步数至少为\(k\)

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

限制

\(1\le n,m\le 5\cdot 10^5\)

\(1\le k\le 10^{12}\)

\(1\le a_i,b_i\le 2\cdot \max(n,m)\)




思路

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

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


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

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

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

可以得到以下两式:

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

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


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

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

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

检查是否\(x-sum\ge 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<<'\n';
}
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

posted @ 2021-03-24 19:26  StelaYuri  阅读(113)  评论(0编辑  收藏  举报