摸摸(2023 激励计划评分 9)

这篇题解使用了数学方法, 解决本题。

思路

首先,容易发现操作一最多进行一次,之后的操作一均可用若干操作二等价代替。具体证明请参考 官方题解,这里不再赘述。

在本题解中,下标从 开始,且所有的 都保证 为整数且

让我们重点关注 数组的变化过程中的 次——对 进行操作一前最后一刻的样子与结束后的样子。

进行操作前最后一刻, 数组为若干个 数组元素依次相加得到的,可记作:

。其中 表示在进行唯一的操作一之前进行了 次操作二。

在对 进行操作一后,记操作二后的数组为

记操作结束后的数组 为操作 前的 数组与若干个 元素依次相加得到,即

其中 表示进行操作一之后操作二的次数。

我们先假设答案为 Yes,即 数组与 相同。由此,我们已知 ,列出方程:

可解得:

尝试解这个方程。当出现无法整除或解是负数的情况时直接输出 No
但是还有一个问题:当 时,分母为 ,该方程无意义。我们先打上标记,一会再说。

之后,我们从每个 得到的有意义的解中任意选一个,在 数组中依次代回验证,不符合时说明没有一组符合的解适用于任意的 ,此时输出 No

但是还有一个问题,当任意 时(即 是回文序列),无法找到一组有意义的解。不过此时我们可以证明操作一可以被若干操作二平替,因此将 数组的每个元素依次相加若干次必然可以得到 。因此,我们说,对于任意 都有 整除且 都相等。以此为条件判断即可。

代码

以下是代码。不过这份写的非常混乱,要考虑的细节问题过多,还请各位见谅。我会以尽可能清晰的注释帮助大家理解每一部分。

#include<bits/extc++.h>
using namespace std;
namespace pbds=__gnu_pbds;
using ui=unsigned int;
using uli=unsigned long long int;
using li=long long int;
#define NO {cout<<"No\n";goto nexturn;}
int main(void){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    size_t T;
    cin>>T;
    while (T--){
        size_t n;
        cin>>n;
        vector<li> b(n),t(n);
        for (li& i:t) cin>>i;
        for (li& i:b) cin>>i;
        vector<pair<li,li>> pr(n>>1);pair<li,li> useful(-1,-1); // first存x,second存y
        if (n==1){cout<<(b[0]%t[0]?"No\n":"Yes\n");goto nexturn;} // n=1时特判
        for (size_t i=0,j=n-1;i<pr.size();++i,--j){ // j=n-i-1
            if (t[i]==t[j]) // t[i]=t[j],分式无意义
                pr[i]={-1,-1}; // 特殊标记
            else{
                if ((b[i]-b[j])%(t[i]-t[j])) NO // 不能整除
                pr[i].first=(b[i]-b[j])/(t[i]-t[j]); // x
                if ((b[i]-pr[i].first*t[i])%(t[i]+t[j])) NO // 同理,求y时判断是否整除
                pr[i].second=(b[i]-pr[i].first*t[i])/(t[i]+t[j]); // y
                if (pr[i].first<0||pr[i].second<0) NO;  // 解<0,不合理
            }
        }
        for (pair<li,li> const& i:pr) if (i!=useful){  // 寻找一组有意义的解
            useful=i;break;
        }
        if (useful==pair<li,li>(-1,-1)){  // 没找到
            li times=b[0]/t[0]; 
            for (size_t i=0;i<n;++i) if (b[i]%t[i]||b[i]/t[i]!=times) NO // 判断是否整除、商均相等
        }else for (size_t i=0,j=n-1;i<(n+1>>1);++i,--j)
            if (b[i]!=useful.first*t[i]+useful.second*(t[i]+t[j])
                ||b[j]!=useful.first*t[j]+useful.second*(t[i]+t[j])) NO // 代回验证
        cout<<"Yes\n";
        nexturn:;
    }
    return 0;
}
posted @ 2023-10-08 19:18  MrPython  阅读(9)  评论(0)    收藏  举报  来源