1. 程式人生 > 實用技巧 >agc_045f Division into Multiples

agc_045f Division into Multiples

agc_045f Division into Multiples

https://atcoder.jp/contests/agc045/tasks/agc045_f

Tutorial

https://www.cnblogs.com/weiyanpeng/p/13095975.html

https://img.atcoder.jp/agc045/editorial.pdf

首先可以通過化簡使得\(\gcd(A,C)=1,\gcd(B,C)=1\),具體實現可以參考程式碼

我們稱\((x,y)\)為好的若\(Ax+By\equiv 0 \mod C\),我們想要找出所有極小的好\((x,y)\).

\(D=\dfrac AB \mod C\)

,顯然\((0,C)\)是好的,對於\(1\le i\le C\),\((i,C-Di \mod C)\)是好的.所以我們想要找出所有\(i\),滿足對於任意\(j<i\)\(Di \mod C > Dj \mod C\) .

可以考慮一個高\(D\),寬\(C\)的迴圈網格,初始位於\((0,0)\),之後每次移動\((1,1)\),在\(y\)座標等於\(0\)時紀錄\(x\)座標,假如此時紀錄\(x\)大於之前紀錄的所有座標,那麼\((\dfrac{now}D,C-x)\)就是極小的好的二元組,其中\(now\)表示移動次數.

考慮加速這個過程,設當前網格圖高為\(H\)寬為\(W\)

,若\(W\ge H\),那麼首先會紀錄\((H,0)\),發現此時就不需要關心\(x<H\)的部分,所以可以將寬縮小為\(W-H\),\(W<H\)時也可以類似的處理,於是這就變成了一個類似歐幾里得演算法的流程.

觀察上面的演算法,發現好的\((x,y)\)若按\(x\)大小排序,則是\(O(\log V)\)個等差數列的形式,其中\(V=10^9\).且滿足\(x_i-x_{i-1}\le x_{i+1}-x_i,y_i-y_{i-1}\ge y_{i+1}-y_i\)

發現這就是一個凸包的形式,而我們求最多的可以劃分的好的二元組個數其實就是所有好的二元組的\(\min\)卷積的形式,也就是Minkowski和.所有我們只需要對於每個等差數列求解即可.

具體過程可以用二分答案解決,參考程式碼

Code

https://www.cnblogs.com/weiyanpeng/p/13095975.html

https://atcoder.jp/contests/agc045/submissions/14150436

https://atcoder.jp/contests/agc045/submissions/14060512

#include <cstdio>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char gc() {
//	return getchar();
	static char buf[100000],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
	scanf("%d",&x); return;
	x=0; int f=1,ch=gc();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
	x*=f;
}
template<class T> inline bool Cmax(T &x,T y) {return x<y?x=y,1:0;} 
typedef long long ll;
int T,A,X,B,Y,C,D;
vector<int> pos,cnt;
int gcd(int a,int b) {return b==0?a:gcd(b,a%b);}
int exgcd(int a,int b,int &x,int &y) {
	if(b==0) {x=1,y=0; return a;}
	int d=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
inline int inver(int a,int mod) {
	int x,y,d=exgcd(a,mod,x,y);
	if(x<0) x+=mod;
	return x;
}
inline ll lowdiv(ll a,ll b) {return a/b-(a%b&&(a^b)<0);}
void init() {
	int d=gcd(A,B);
	A/=d,B/=d,C/=gcd(d,C);
	for(int _=0;_<2;++_) {
		d=gcd(A,C);
		C/=d,A/=d;
		int t=gcd(B,d);
		B/=t,Y/=d/t;
		swap(A,B),swap(X,Y);
	}
}
void getpos() {
	pos.clear(),cnt.clear();
	int W=C,H=D,now=0,r=inver(D,C);
	while(W) {
		int d=W/H;
		pos.push_back((ll)now*r%C),cnt.push_back(d); 
		now+=d*H;
		W%=H; if(W==0) break;
		H%=W; if(H==0) H=W;
	}
	pos.push_back(C);
}
inline int gety(int x) {return x==0?C:(C-(ll)x*D%C)%C;}
int main() {
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	rd(T);
	for(int kase=1;kase<=T;++kase) {
		rd(A),rd(X),rd(B),rd(Y),rd(C);
		init();
		if(C==1) {printf("%d\n",X+Y); continue;}
		D=(ll)A*inver(B,C)%C;
		getpos();
		int an=0;
		for(int i=0;i<cnt.size();++i) {
			int xl=pos[i],xr=pos[i+1],yl=gety(xl),yr=gety(xr);
			int dx=(xr-xl)/cnt[i],dy=(yl-yr)/cnt[i];
			int l=0,r=X+Y,re=-1;
			while(l<=r) {
				int mid=((ll)l+r)>>1;
				ll p=lowdiv(X-(ll)xl*mid,dx),q=lowdiv(Y-(ll)yr*mid,dy);
				if(p>=0&&q>=0&&p+q>=(ll)cnt[i]*mid) re=mid,l=mid+1;
				else r=mid-1;
			}
			Cmax(an,re);
		}
		printf("%d\n",an);
	}
	return 0;
}