CF1500B(Div.1) Two chandeliers

Label

EXCRT+二分答案

Description

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

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

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

其中 1 ≤ n , m ≤ 5 × 1 0 5 , 1 ≤ k ≤ 1 0 12 , 1 ≤ a i , b i ≤ 2 × m a x ( 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

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

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

其中 i , j i,j i,j分别为 a i a_i ai在数列 a , b a,b a,b的位置下标。

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

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

复杂度: O ( n l o g V ) O(nlogV) O(nlogV),其中 V V V为可能的最大答案(此题的复杂度来源于二分答案,由于EXGCD可以只进行一次,所以整个求 x i x_i xi的过程复杂度为 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值