P9714 「QFOI R1」摸摸 题解
本题有
前言:
赛时看到题的第一眼想法就是线性...以至于代码都码了一半才看到数据范围,额,似乎可以
赛后也反馈了这一问题,据出题人的说法是:这种做法是已知的,只不过考量到这是面对基础赛出的题目,所以不准备加强数据。所以这个线性做法就当是给各位加加餐了吧(
题解正题:
首先,这种一看就能用爆搜做的题,如果数据范围不支持你跑爆搜,那么其本身一定有着一些有趣的性质。
这种有趣的性质出现在了操作
以下我把“关于对称中心对称的两个数”简称为“对称的数”。
我们要差值为
我们要让
仅有口述还是太苍白了,我们举个例子来体现一下:
n=5
t[]=1,1,1,2,3; b[]=6,5,4,7,10
//dist 代表 t 对称的数的差值,disb 代表 b 对称的数的差值
dist[]=2,1; disb[]=4,2
//此时我们执行两次操作 2,就可以使 a 的每组差值与 b 相同。
a[]=2,2,2,4,6
上述例子我们能通过两次操作 NO。
如果能的话,我们便让 YES;不能,则答案依然是 NO。
//同上例
a[]=2,2,2,4,6;
//让 t 自增
t[]=4,3,2,3,4;
//执行 1 次操作 2
a[]=6,5,4,7,10; (==b[])
//YES
当然这里还是要提一嘴,执行
知道了做法,剩下的本质上就是一些乘除取模运算的事了。显然的是,我们不用暴力地去枚举某个阶段操作
这种做法的瓶颈仅在于需要遍历数组。时间复杂度为
\text{Code:}
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 3005
#define endl '\n'
#define ll long long
using namespace std;
int n;
int b[maxn], t[maxn], a[maxn];
void run()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> t[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
int dist, disb;
for(int i = 1; i <= n / 2; i ++) //不断寻找 b 与 t 的差值,直到找到一组不全为 0 的为止
{
dist = t[i] - t[n - i + 1], disb = b[i] - b[n - i + 1];
if(dist != 0 || disb != 0) break;
}
if(dist * disb < 0) {cout << "NO" << endl; return;} //两差值符号不同,那必然不行
if(dist == 0 && disb == 0) //找到最后差值都均为 0,说明 b 与 t 原本就中心对称
{
int mul = b[1] / t[1];
for(int i = 1; i <= n; i ++)
if(t[i] * mul != b[i]) {cout << "NO" << endl; return;}
cout << "YES" << endl; return;
}
if(dist != 0)
{
if(disb % dist) {cout << "NO" << endl; return;}
int mul = disb / dist; //执行 mul 遍操作 1
for(int i = 1; i <= n; i ++) a[i] = mul * t[i];
}
if(b[1] < a[1] || (b[1] - a[1]) % (t[1] + t[n])) {cout << "NO" << endl; return;}
int mul2 = (b[1] - a[1]) / (t[1] + t[n]);
//t 自增后执行 mul2 便操作 1。这里用数组中的第一个数来计算 mul2 值
for(int i = 1; i <= n; i ++)
{
a[i] += mul2 * (t[i] + t[n - i + 1]);
if(a[i] != b[i]) {cout << "NO" << endl; return;}
}
cout << "YES" << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while(t --) run();
return 0;
}