agc_045f Division into Multiples

agc_045f Division into Multiples

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

Snipaste_2020-07-01_11-39-08.png

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;
} 

posted @ 2020-07-01 12:00  LJZ_C  阅读(369)  评论(0编辑  收藏  举报