CodeForces - 1244C

题目传送门

题解:

题目大意:
给定long long范围内整数n,p,w,d
求满足方程wx+dy=p的一组和小于等于n的自然数解
\(1\leq n\leq10^{12},0\leq p \leq10^{17},1\leq d<w \leq10^{5}\)

题干给定的信息很容易联想到使用扩展欧几里得算法求二元一次不定方程的整数解,但是本题限制了x,y的和应大于等于0,小于等于n,所以求出特解后还需调整解的大小。而调整的过程较为麻烦,所以本题中我们可以枚举最小的y。

为什么枚举最小的y,因为取最小的y能使x+y的和也取到最小。

为什么枚举x,y可行。首先在n范围内枚举x,y显然不可行,因为n的大小达到了\(10^{12}\)但我们可以在w的数据规模内确定合法的解是否存在。

枚举的理论依据如下:求一次不定方程整数解实际上也是解线性同余方程,解\(wx+dy=p\)等价于解同余方程\(wx\equiv p-dy(mod\,w)\),即找到一个y使\(\,p-dy\,\)被w整除

而在递推过程中如果出现两个y值对应的 \((p-dy)\%w\)相等,此后枚举的\((p-dy)\%w\)将出现循环。因此,根据鸽巢原理,至多迭代w+1次就会出现循环节,如果在第一个循环节内没有找到符合条件的y,可以确定在1-n范围内都不存在符合条件的y。如果找到了y,这个y即是我们寻找的一个解。

代码:

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    ll n, p, w, d, x=-1, y=-1;
    cin >> n >> p >> w >> d;
    map<ll, ll> m;
    for (ll i = 0; i * d <= p; i++)
    {
        ll t = p - i * d;
        if (t % w == 0)
        {
            x = t / w;
            y = i;
            break;
        }
        else
        {
            if (m[i * d % w] == 1)
                break;
            else
                m[i * d % w] = 1;
        }
    }
    if(x!=-1 && x+y<=n)
        cout << x << ' ' << y << ' ' << n - x - y<<endl;
    else
        puts("-1\n");
    return 0;
}

posted @ 2021-05-22 00:06  ambrumf  阅读(60)  评论(0编辑  收藏  举报