CF1500B(Div.1) Two chandeliers

本文详细介绍了如何解决一个涉及序列异同的复杂算法问题,通过EXCRT(扩展中国剩余定理)结合二分搜索,求解最小满足条件的正整数。文章深入探讨了算法思路,包括利用EXCRT求解通解,以及二分答案的检查策略,最终实现了O(nlogV)的时间复杂度解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Label

EXCRT+二分答案

Description

原题链接:https://codeforces.com/problemset/problem/1500/B

抽象题意:给定长度分别为n,mn,mn,m的、序列内元素互不相同的两个不同的正整数序列a,ba,ba,b与正整数kkk,求最小的正整数qqq,满足:

∑i=1q[a((i−1)modn+1)≠b((i−1)modn+1)]≥k\sum_{i=1}^{q}[a_{_{((i-1)modn+1)}}\ne b_{_{((i-1)modn+1)}}]\ge ki=1q[a((i1)modn+1)=b((i1)modn+1)]k

其中1≤n,m≤5×105,1≤k≤1012,1≤ai,bi≤2×max(n,m)1\leq n,m\leq5\times 10^5,1\leq k\leq 10^{12},1\leq a_i,b_i\leq 2\times max(n,m)1n,m5×105,1k1012,1ai,bi2×max(n,m).

Solution

由于只有存在ai=bja_i=b_jai=bj的情况下才会对答案发生影响(ai≠bja_i\neq b_jai=bj时,其对给定∑\sum的贡献一定为000),故对于任意一个∈a\in aa的元素aia_iai,我们只需考虑其是否在bbb内也出现过(设其为bjb_jbj);如果出现过,是否会出现a((i−1)modn+1)=b((i−1)modn+1)a_{_{((i-1)modn+1)}}=b_{_{((i-1)modn+1)}}a((i1)modn+1)=b((i1)modn+1)的情况。

假设i=xi=xi=x满足上式,那么一定有:
{x≡i(modn)x≡j(modm)\begin{cases}x\equiv i(modn)\\x\equiv j(modm)\end{cases}{xi(modn)xj(modm)

其中i,ji,ji,j分别为aia_iai在数列a,ba,ba,b的位置下标。

有了这个东西,我们便可以利用EXCRT(其实两个方程可以直接利用求EXCRT的原理进行EXGCD推导)求出所有xxx的通解形式,进而得出两盏灯颜色同为aia_iai(见原题题意)的所有时刻。

至于求qqq的方法,由于qqq越大越容易满足给定条件,故我们直接进行答案越小越优的二分答案即可(此题答案不会爆long long)。在二分答案的check函数里,当q=midq=midq=mid时,我们枚举所有aia_iai,利用之前求得的xix_ixi,计算第1∼q1\sim q1q天内两盏灯颜色同为aia_iai的天数did_idi,最后直接将mid−∑i=1ndimid-\sum_{i=1}^{n}d_imidi=1ndikkk比较即可。>k>k>k则说明最优答案≤mid\leq midmid,反之则大于。

复杂度:O(nlogV)O(nlogV)O(nlogV),其中VVV为可能的最大答案(此题的复杂度来源于二分答案,由于EXGCD可以只进行一次,所以整个求xix_ixi的过程复杂度为O(n)O(n)O(n))。

Code

#include<cstdio>
#include<iostream>
#define ri register int
#define ll long long
using namespace std;

const int MAXN=1e6+20;
int N,M,cola[MAXN],colb[MAXN];
ll K,plaa[MAXN<<1],plab[MAXN<<1],ans[MAXN],A,B,Mi,G,C,x,y,xi,yi,l,r,mid,nowsame;

ll Gcd(ll a,ll b) { return ((b==0)?a:Gcd(b,a%b)); }
ll Lcm(ll a,ll b) { return (a/Gcd(a,b))*b; }
ll Msc(ll a,ll b,ll MOD)
{
	ll tot=0;
	for(;b;b>>=1,a=(a+a)%MOD) 
		if(b&1)	tot=(tot+a)%MOD; 
	return tot;
}

void Exgcd(ll a,ll b)
{
	if(b==0) { x=1,y=0; return; }
	Exgcd(b,a%b);
	xi=x,yi=y;
	x=yi,y=xi-(a/b)*yi;
}

bool check(ll tot)
{
	nowsame=0;
	for(ri i=1;i<=N;++i)
	{
		if(ans[i]==0||ans[i]>tot)	continue;
		nowsame+=1+(tot-ans[i])/Mi;
	}
	if(tot-nowsame>=K)	return true;
	return false;
}

int main()
{
	scanf("%d%d%lld",&N,&M,&K);
	for(ri i=1;i<=N;++i)
	{
		scanf("%d",&cola[i]);
		plaa[cola[i]]=(ll)i; 
	}
	for(ri i=1;i<=M;++i)
	{
		scanf("%d",&colb[i]);
		plab[colb[i]]=(ll)i; 		
	}
	Mi=Lcm(N,M);
	Exgcd(N,M);
	for(ri i=1;i<=N;++i)
	{
		if(plab[cola[i]]==0LL)	continue;
		ans[i]=plaa[cola[i]]; C=plab[cola[i]]-ans[i]; G=Gcd(N,M);
		if(C%G!=0) { ans[i]=0; continue; }
		x=(x%(M/G)+(M/G))%(M/G);
		ans[i]=(ans[i]+Msc((C/G)%Mi,Msc(N,x,Mi),Mi))%Mi;
		if(ans[i]>0)	ans[i]%=Mi;
		if(ans[i]<0)	ans[i]=(ans[i]%Mi+Mi)%Mi;
		if(ans[i]==0)	ans[i]=Mi;
	}
	l=1,r=1e18;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(check(mid))	r=mid;
		else	l=mid+1;
	}
	if(check(l))	cout<<l;
	else	cout<<"-1";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值