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\)
可以得到以下两式:
套扩展中国剩余定理即可(由于二分对下标不影响,故这部分需预处理)
其后每次二分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;
}