kmjp's blog

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

Codeforces ECR #095 : F. Equal Product

こういうシンプルな設定の問題、初手どうすればよいか手がつかない。
https://codeforces.com/contest/1418/problem/F

問題

整数N,M,L,Rが与えられる。

  • 1≦X1<X2≦N
  • 1≦Y2<Y1≦M
  • L≦X1*Y1=X2*Y2≦R

各X1について、上記を満たす(X1,X2,Y1,Y2)の組が存在すればそれを答えよ。

解法

X1を総当たりする。X1を定めると、Y1の範囲が定まる。
仮にX1:X2=b:a (a>b)とすると、Y1:Y2=a:bでなければならない。
よって、範囲内にY1の範囲内にaの倍数となるものがあるかをチェックしよう。

この際、Yの範囲に対し、各整数kに対しkの倍数となるものを列挙しておくことを考える。
X1をずらすとYの範囲も徐々にずれるので、尺取り法の要領でdequeを使い上記kの倍数毎の有効なYをちょっとずつずらすことで、Yの列挙を高速化できる。

ll N,M;
ll L,R;
ll A[202020],B[202020];
vector<int> D[202020];
deque<int> cand[202020];
set<int> CD;

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>M;
	cin>>L>>R;
	
	for(x=1;x<=200000;x++) {
		A[x]=max(1LL,(L+x-1)/x);
		B[x]=min(M,R/x);
		for(y=x;y<=200000;y+=x) D[y].push_back(x);
	}
	int LY=M+1,RY=M+1;
	for(x=1;x<=N;x++) {
		
		if(A[x]>B[x]) {
			cout<<-1<<endl;
			continue;
		}
		
		
		while(A[x]<LY) {
			LY--;
			FORR(d,D[LY]) {
				if(cand[d].empty()) CD.insert(d);
				cand[d].push_front(LY);
			}
		}
		while(B[x]<RY-1) {
			RY--;
			FORR(d,D[RY]) {
				cand[d].pop_back();
				if(cand[d].empty()) CD.erase(d);
			}
		}
			
		vector<int> ret;
		FORR(a,D[x]) {
			auto it=CD.lower_bound(a+1);
			if(it!=CD.end()) {
				int b=*it;
				y=cand[b][0];
				if(1LL*x/a*b<=N) {
					ret={x,y,1LL*x/a*b,1LL*y/b*a};
					break;
				}
			}
		}
		
		if(ret.empty()) {
			cout<<-1<<endl;
		}
		else {
			cout<<ret[0]<<" "<<ret[1]<<" "<<ret[2]<<" "<<ret[3]<<endl;
		}
		
		
	}
}

まとめ

GよりだいぶAC数が少ない。