链接:https://ac.nowcoder.com/acm/contest/910/E
来源:牛客网

题目描述
现在有很多砝码,质量为w的0次方、1次方……n次方,每个砝码都只有一个。还有一个天平,可以在两端放置砝码和重物。现在要用这些砝码搭配出相等于重物的质量m,也可以把若干个砝码和重物一起放在天平的一侧,来平衡这个天平。

首先我的解法比较骚气,类似模拟,所以(因果关系?)先把w=2的情况特判掉(不要问我w=2为什么一定成立)。
然后我们确定一个边界值a[n],使得a[n]恰好大于等于m。
其中a[i]为第i个砝码的质量,b[i]为前i个砝码的质量和。

	int cnt=1;
	while(a[cnt]<m){
	    a[++cnt]=a[cnt-1]*w;
	    b[cnt]=b[cnt-1]+a[cnt];
	}

通过等比数列求和可得w>=3时 b[n]<=a[n+1]/2
因此边界值之后的砝码一定是我们用不到的,因为在w大于等于三时一定有a[n+1]>=b[n]*2,因为b[n]>m,所以也就是a[n+1]-b[n]>m,因此就算把之前所有的砝码减去也无法到达m,再之后的砝码亦然。

然后我们从边界值开始逆序判断:对于当前这个砝码,我是否需要添加。
设我当前位置为pos,砝码为第cnt个,则前(cnt-1)个砝码所能控制的最大范围就是b[cnt-1]。
我们对当前砝码进行判断:
如果我与m的距离小于等于于b[cnt-1],则无需 加上/减去 砝码,因为我就算用上接下来所有砝码也无法平衡。
如图:如果pos加上A[cnt],那与m的距离显然会大于b[cnt-1]。所以该操作肯定是非法的,无需执行。

而如果这个操作能让pos与m的距离小于等于b[cnt-1],那就说明当前的pos与m的距离在b[cnt-1]的控制范围外(想一想,为什么?),图示如下:

所以我们必须进行该操作。
这时,如果两种情况皆不合理,也就是m在中间,那我就算加不加当前砝码都会与其距离大于b[cnt-1],这就是无解的情况,那我们的while就会停下来等死(挂机到a[1]自动退出)hhh。

int flag=0;
long long pos=0;
while(cnt){
    if(abs(pos+a[cnt]-m)<=b[cnt-1]){//如果加上该砝码能使我们进入b[cnt-1]的控制范围
        pos+=a[cnt];
    }
    else if(abs(pos-a[cnt]-m)<=b[cnt-1]){//如果减去该砝码能使我们进入b[cnt-1]的控制范围
        pos-=a[cnt];
    }
    if(pos==m){//判断是否已经等于m
        flag=1;
        break;
    }
    cnt--;
}

完整代码:

#include<bits/stdc++.h>
using namespace std;
long long w,m;
long long a[233];//记录第i个砝码的质量
long long b[233];//记录前i个砝码的质量和
int main()
{
    int t;
    cin>>t;
    while(t--){
        scanf("%lld%lld",&w,&m);
        if(w==2)//巧妙绝伦的特判
        {
            cout<<"YES"<<endl;
            continue;
        }
        a[1]=1;
        b[1]=1;
        int cnt=1;
        while(a[cnt]<m){
            a[++cnt]=a[cnt-1]*w;
            b[cnt]=b[cnt-1]+a[cnt];
        }
        int flag=0;
        long long pos=0;
        while(cnt){
            if(abs(pos+a[cnt]-m)<=b[cnt-1]){
                pos+=a[cnt];
            }
            else if(abs(pos-a[cnt]-m)<=b[cnt-1]){
                pos-=a[cnt];
            }
            if(pos==m){
                flag=1;
                break;
            }
            cnt--;
        }
        if(flag)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
 
    }
    return 0;
}