Link
题意
给定两个长度为 n 的数组 a1∼n,b1∼n,每次可以进行如下操作:
- 对于每个 i∈[1,n],ai←∣ai−bi∣。
- 对于每个 i∈[1,n],交换 ai,bi。
求多次操作后是否能使所有 bi 变成 0。
分析
假如 ai=5,bi=3,可以发现它们是这样变化的:(5,3)→(3,2)→(2,1)→(1,1)→(1,0)→(0,1)→(1,1)→(1,0)→(0,1)→……
在 (ai,bi) 变成 (x,0) 后,它就会开始循环:(x,0)→(0,x)→(x,x)→(x,0),循环节为 3,于是我们就可以记录 ci,j 表示 (ai,bi) 经过 3k+j(k∈N+) 次操作后,bi 能否变成 0,若存在 j 满足 ci,j(i∈[1,n]) 全为 1,那么就可以使所有 bi 变成 0。
如果直接暴力计算出 bi 求 ci,j 的话,肯定会超时,需要优化。我们假设 ai 远大于 bi,那么 ai−bi 一定是正的,假设如果连续多次减法都不出现负数的话,变化的过程就是这样的 (ai,bi)→(bi,ai−bi)→(ai−bi,ai−2bi)→(ai−2bi,bi)→(bi,ai−3bi)→(ai−3bi,ai−4bi)→(ai−4bi,bi)→(bi,ai−5bi)→……
可以发现从 (bi,ai−bi) 开始也有一个类似循环 (bi,ai−(2k−1)bi)→(ai−(2k−1)bi,ai−2kbi)→(ai−2kbi,bi)→(bi,ai−(2k+1)bi)。所以我们可以一次性将 (ai,bi) 变为 (bi,ai−(2k+1)bi),即一次性将 ai 减去奇数个 bi,当然前提是这个差大于 0,最终操作次数会增加 3k+1 步。每次用最大的 k 来操作,令值域为 S,复杂度就为 O(nlogS)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+10;
int t,n,a[N],b[N],c[N][3];
int main(){
t=read();
while(t--){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
c[i][0]=c[i][1]=c[i][2]=0;
}
for(int i=1;i<=n;i++){
b[i]=read();
c[i][0]=c[i][1]=0;
}
int ans1=1,ans2=1,ans3=1;
for(int i=1;i<=n;i++){
if(a[i]==0&&b[i]==0){
c[i][0]=c[i][1]=c[i][2]=1;
}
else{
int u=0,d;
while(b[i]){
if(a[i]<=b[i]||a[i]/b[i]<2){
d=abs(a[i]-b[i]);
a[i]=b[i];
b[i]=d;
u=(u+1)%3;
}
else{
int p=a[i]/b[i],q;
if((p&1)==0)
p--;
u=(u+(p-1)/2*3+1)%3;
q=a[i]-p*b[i];
a[i]=b[i];
b[i]=q;
}
}
c[i][u]=1;
}
ans1&=c[i][0];
ans2&=c[i][1];
ans3&=c[i][2];
}
if(ans1||ans2||ans3)
puts("YES");
else
puts("NO");
}
return 0;
}