kmjp's blog

競技プログラミング参加記です

Codeforces #707 Div1 : B. Two chandeliers

難しくはないけど細かいところが面倒な問題。
https://codeforces.com/contest/1500/problem/B

問題

2つの整数列A,Bが与えられる。
各数列内では、同じ値は1回しか登場しない。
i個目の整数ペアを、A[i%|A|]とB[i%|B|]とする。
ペアの値が異なるのがK回目となるのは、何個目の整数ペアか。

解法

解をRとしたとき、二分探索で求めることを考える。
考えた整数ペアのうち、R個目までのペアうち、値が一致するペアの登場回数を求めよう。
これは、A,B両方に登場する値について、R個目までのペアにその値のペアが何回登場するかを求めればよい。

中国人剰余定理を用いれば、初回両者がペアになるタイミングとその後の周期がわかるので、その値のペアの登場回数が計算できる。

int N,M;
ll K;
int A[505050],B[505050];

int P[1010101];
int Q[1010101];
pair<ll,ll> C[1010101];
ll ext_gcd(ll p,ll q,ll& x, ll& y) { // get px+qy=gcd(p,q)
	if(q==0) return x=1,y=0,p;
	ll g=ext_gcd(q,p%q,y,x);
	y-=p/q*x;
	return g;
}

pair<ll,ll> crt(ll a1,ll mo1,ll a2,ll mo2) { // return (x,y) y=lcm(a1,a2),x%mo1=a1,x%mo2=a2
	ll g,x,y,z;
	g=ext_gcd(mo1,mo2,x,y);
	a1=(a1%mo1+mo1)%mo1;a2=(a2%mo2+mo2)%mo2;
	if(a1%g != a2%g) return pair<ll,ll>(-1,0); // N/A
	__int128_t lcm=mo1*(mo2/g);
	if(lcm<mo1) return pair<ll,ll>(-2,0); // overflow
	
	__int128_t v=a1+((a2-a1)%lcm+lcm)*x%lcm*(mo1/g);
	return make_pair(((v%lcm)+lcm) % lcm,lcm);
}

ll fail(ll d) {
	int i;
	ll tot=0;
	FOR(i,1010000) if(P[i]>=0&&Q[i]>=0) {
		if(C[i].first==0&&C[i].second==0) {
			C[i]=crt(P[i],N,Q[i],M);
		}
		if(C[i].first<0) continue;
		auto& p=C[i];
		if(p.first<=d) {
			ll x=(d-p.first)/p.second;
			while(p.first+x*p.second>=d) x--;
			while(p.first+(x+1)*p.second<d) x++;
			tot+=x+1;
		}
	}
	return d-tot;
}


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	MINUS(P);
	MINUS(Q);
	cin>>N>>M>>K;
	FOR(i,N) {
		cin>>A[i];
		P[A[i]]=i;
	}
	FOR(i,M) {
		cin>>B[i];
		Q[B[i]]=i;
	}
	
	ll ret=1LL<<60;
	for(i=59;i>=0;i--) if(fail(ret-(1LL<<i))>=K) ret-=1LL<<i;
	cout<<ret<<endl;
	
}

まとめ

まぁでも本番割とすんなり解けてたようだ。