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