2026牛客寒假营4 D东风谷早苗与博丽灵梦

东风谷早苗与博丽灵梦

https://ac.nowcoder.com/acm/contest/120564/D

2026牛客寒假算法基础集训营4 D题 东风谷早苗与博丽灵梦

前置知识 数论裴蜀定理 exgcd 龟速乘(防爆long long

首先我们来观察一下这道题是两个人相遇求最短时间,那么写成方程就是aX+sY=x的问题

这里总路程是x,首先我们肯定是要能够构造出一个解的,根据裴蜀定理,我们可以知道上面的二元一次方程要有整数解的话,x%gcd(a,s)=0,否则就不能构造出来两个整数解,我们先根据这个进行第一步判断同时求出ax0+sy0=exgcd(a,s) 中的x0和y0

那么回过来看问题我们实际上想要求的是aX+sY=x 那么我们对上面的公式变形,记g=exgcd(a,s) ** 即变为 ax0x/g + sy0x/g = x 那么现在x0和y0都不一定都是正整数,我们想要对它进行变形,因为我们想要求最小的变化这样才能找到最精确的整数点**

这里需要最小化步长 对于x来说dx = s/g 为什么呢?因为对于 ax + sy = c来说 a(x+dx) + s(y-dy) = c和原式没区别,而这可以改变X和Y,我们想要最小微调就需要改变最小步长,所以我们后面计算出来的值都要对dx或者dy取模

接下来我们就想要求 aX + sY = x中的X和Y了 X+kdx就是c1 Y-kdy就是c2 这里的X和Y是一组特解其实我们只要求一个就行了,另一个可以通过公式求出来,这里我们求X

那么 X 通过观察上面的公式可以发现就是 x0*x/g 那么这里为了防止爆longlong 使用龟速乘

X = (cheng(x0,x/g,dx)%dx+dx)%dx; 

这里进行负数化最小正整数的取模,因为x0可能是负数

这里又有一个关键判断,如果说最小的X*a都能大于x的话就说明Y只能是负数而这是不对的,所以要特判

现在我们求出了X 接下来就是求Y 那么我们有上面的公式所以直接变形

Y = (x-a*X)/s;

好了我们现在有了X也有了Y 接下来就是另一个问题了 我们想要时间最短怎么办呢,在相遇问题中时间最短取决于两个人所用时间的最大值,也就是我们要求两个最大值的最小值,那我们知道X增大Y必然减小,为了让最大值最小就要让X和Y尽可能靠近,我们不妨假设 X+kdx = Y-kdy 变形就可以得出k = (Y-X)/(dx+dy)

这里是点睛之笔我们不需要通过二分,直接相除就可以得到k,那因为除法向下取整了,所以答案也可能是k+1,我们需要进行判断 这里我们初始化c1 = X+kdx c2 = Y-kdy cc1 = X+(k+1)dx cc2 = Y-(k+1)dy 因为答案必然在k和k+1之间,而k+1可能把c2变成负数所以需要特别判断

如果cc2<0那么直接输出c1 和 c2 就是答案

否则,我们需要在max(c1,c2) 和max(cc1,cc2)中间取最小值对应的值进行输出

好耶!完结撒花!!! ヽ(✿゚▽゚)ノ✿゚✿゚✿゚ alt

附上源代码:

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
#define endl '\n'

ll exgcd(ll a,ll b,ll &x,ll& y)
{
     if(b==0)
     {
     	x=1,y=0;
     	return a;
     }
     ll res=exgcd(b,a%b,y,x);
     y-=a/b*x;
     return res;
}
ll inv(ll n,ll p)
{
	ll x,y;
	ll bo=exgcd(n,p,x,y);
	if(bo!=1)return -1;
	else
	{
		return (x%p+p)%p;
	}
}
ll cheng(ll a,ll b,ll p)
{
	ll res=0;
	a%=p;
	while(b)
	{
		if(b&1)res=(res+a)%p;
		a=(2*a)%p;
		b>>=1;
	}
	return res;
}
ll n,m,k;
void solve()
{
	ll x,a,s;
	cin>>x>>a>>s;
	ll x0,y0;
	ll g=exgcd(a,s,x0,y0);//ax0+sy0=gcd(a,s);
	if(x%g!=0)
	{
		cout<<"No"<<endl;
	}else
	{
	ll dx=s/g;//最小步长 x=x0+k*dx y=y0-k*dy 之所以dx是s/g就是因为要相互抵消掉,dy同理
	ll dy=a/g;
    ll X=(cheng(x0,x/g,dx)%dx+dx)%dx;//最小满足正整数X aX+sY=x
    if(X>x/a)//如果最小的X都不能满足,即Y必须是正数则无解
    {
    	cout<<"No"<<endl;
    }else
    {
    	ll Y=(x-a*X)/s;
    	//如果我们想要两个人相遇时间最短,那么这个时间取决于两个人走的最慢的那个人
    	//也就是我们希望两个人的耗时最大的能尽可能小,那么,只有两个耗时最相近的时候
    	//他们的耗时最大值最小
    	//那么我们假设可以相等
    	//即  X+kdx = Y-kdy  ->  k(dx+dy)=Y-X
    	ll k = (Y-X)/(dx+dy);
    	//除法向下取整 所以k+1也可能是正解,需要判断取解
    	// k
        ll c1=X+k*dx;
        ll c2=Y-k*dy;
        //k+1
        ll cc1=X+(k+1)*dx;
        ll cc2=Y-(k+1)*dy;
        cout<<"Yes"<<endl;
        if(cc2<0)//如果k+1 让c2变成负数那答案就是k
        {
        	cout<<c1<<" "<<c2<<endl;
        }else
        {
        	if(max(c1,c2)<=max(cc1,cc2))
        	{
        		cout<<c1<<" "<<c2<<endl;
        	}else
        	{
        		cout<<cc1<<" "<<cc2<<endl;
        	}
        }
    }
	}
}

int main()
{
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	ll T=1;
	cin>>T;
	while(T--)
	{
		solve();
	}
}
全部评论
通俗易懂,讲的比官解好,实现也比官解好
点赞 回复 分享
发布于 02-10 19:35 浙江

相关推荐

评论
5
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务