\(\text{C - Nezzar and Symmetric Array}\)
题目
简要题意
有一个长度为 \(2n\) 的数列 \(a\),其中每个数字都 互不相同,并且它还满足这样的条件:
- 对于任意 \(1\le i\le 2n\) 都存在一个 \(j\) 且 \(1\le j\le 2n\),有 \(a_i=-a_j\)。
但你不知道这个数列,你只知道一个长度为 \(2n\) 的数列 \(d\),有 \(d_i=\sum_{j=1}^{2n}|a_i-a_j|\)。
数据范围与提示
有多组数据 \(\le 10^5\),\(1\le n\le 10^5,\ 0\le d_i\le 10^{12}\),且 \(\sum n\le 10^5\)。
解法
#1. 一种方法
看到了 “某点与其它点的距离之和”,我们不禁将数轴画了出来。
显然相对原点对称的两点是等价的,而且要求数字 互不相同。那就首先无脑排个序,判断一下所有 \(d\) 值是否只出现两次,不是就是 no
了。
假设我们将 \(a\) 数列中 \(>0\) 的项拉出来再排个序,得到新的 \(a\) 数列,再把 \(d\) 数列中重复的项删掉。
初中老师告诉我们,\(a_i\) 对应着 \(d_i\),而且 \(d_1\) 值就是 \(2\times\sum_{i=1}^{n}a_i\)。
对于 \(d_{i},(i>1)\),我们思考它与 \(d_{i-1}\) 之差代表了什么。显然对于 \(1\le j<i\),在计算距离时 \(d_i\) 比 \(d_{i-1}\) 都多算了 \(2(a_i-a_{i-1})\)。故 \(d_i-d_{i-1}=2(i-1)(a_i-a_{i-1})\)。
这样我们可以计算出相邻 \(a\) 的差值,最后拿去与 \(d_1\) 检验即可。
需要注意的是,我们不能用差值确定 \(a_1\),但可以算出 \(-na_1+\sum_{i=1}^n a_i\),所以最后只用判断能否整除 \(n\)。
还有几个特判,放在代码里了。
#2. 另一种方法
\(\mathtt{By}\) 胖头鱼学员。
还是假设我们将 \(a\) 数列中 \(>0\) 的项拉出来再排个序,得到新的 \(a\) 数列,再把 \(d\) 数列中重复的项删掉。
由于对称性,你会发现 \(d_n=n\times 2a_n\)。
对于 \(d_{n-1}\),对于在 \([-a_{n-1},a_{n-1}]\) 中的点可以和 \(d_n\) 一样计算,但我们还少算了 \(a_n,-a_n\) 到 \(a_{n-1}\) 的距离即 \(2a_n\)。
所以
总结一下,有
这样可以逆序构造出 \(a_i\),并顺便判断是否合法。
代码
#1. 一种方法
#include <bits/stdc++.h>
using namespace std;
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
typedef long long ll;
const int maxn=2e5+5;
ll d[maxn];
int n;
bool Single_Same() {
for(int i=1;i<n;i+=2)
if(d[i]^d[i+1])
return true;
for(int i=3;i<n;i+=2)
if(d[i]==d[i-1])
return true;
return false;
}
int main() {
rep(T,1,read(9)) {
n=read(9)*2;
rep(i,1,n) d[i]=read(9ll);
sort(d+1,d+n+1);
if(Single_Same()) {
puts("NO"); continue;
}
ll sum=0; bool flag=true;
for(int i=3;i<n;i+=2) {
if((d[i]-d[i-1])%(i-1)==0)
sum+=(d[i]-d[i-1])/(i-1)*(n-i+1)/2;
else {
puts("NO"); flag=false; break;
}
}
if(!flag) continue;
if(d[1]&1) {
puts("NO"); continue;
}
d[1]>>=1;
// 这里需要保证严格大于,排除 a_1=0 的情况
if(d[1]>sum and (d[1]-sum)%(n/2)==0)
puts("YES");
else puts("NO");
}
return 0;
}
#2. 另一种方法
# include <algorithm>
# include <iostream>
using namespace std;
const int kMaxN = 2e5 + 1;
long long T, n, d[kMaxN];
bool Check() {
for (int i = 1; i <= n * 2; i += 2) {
if (d[i] != d[i + 1]) {
return 1;
}
}
return 0;
}
string Solve() {
scanf("%lld", &n);
for (int i = 1; i <= n * 2; i++) {
scanf("%lld", &d[i]);
}
sort(d + 1, d + 2 * n + 1);
if (Check() == 1) {
return "NO\n";
}
long long id = 2 * n, flag = 1, num = 2 * n, sum = 0, last = 0;
while (id) {
if ((d[id] - 2 * sum) % num != 0 || (d[id] - 2 * sum) / num <= 0) {
return "NO\n";
} else {
if (last != 0 && last <= ((d[id] - 2 * sum) / num)) {
return "NO\n";
}
last = ((d[id] - 2 * sum) / num);
sum += ((d[id] - 2 * sum) / num), id -= 2, num -= 2;
}
}
return "YES\n";
}
int main() {
scanf("%lld", &T);
while (T--) {
cout << Solve();
}
return 0;
}
\(\text{D - Nezzar and Board}\)
\(\text{BiBi Time!}\)
感觉自己数学好辣鸡啊。
解法
结论:若 \(x_1=0\) 时,有 \(k\) 被 \(g=\gcd_{i=2}^n x_i\) 整除则可行,否则不可行。
实际上这是个充要条件,我们只需证明以下两个结论即可:
-
可行 \(\Rightarrow\) \(k\) 被 \(\gcd_{i=2}^n x_i\) 整除。
显然当前黑板上每个数都是 \(g\) 的倍数,那么 \(2x-y\) 也是 \(k\) 的倍数。
-
\(k\) 被 \(\gcd_{i=2}^n x_i\) 整除 \(\Rightarrow\) 可行。
先考虑只用 \(x,y\) 两个数。这里有个奇妙的发现 —— \(x\) 实际上是 \(2x-y\) 与 \(y\) 的中点!
于是对于 \(x_1=0,x_2\),我们可以构造出所有 \(x_2\) 的倍数。所以这个结论在 \(n=2\) 时成立。
考虑使用归纳法,已知在 \(n-1\) 情况下结论成立。
设 \(g_0=\gcd_{i=2}^{n-1} x_i,g_1=\gcd_{i=2}^{n} x_i\)。
根据裴蜀定理(它适用于 整数),方程 \(g_0s-x_nt=g_1\) 必有整数解 \(s=s_0+\dfrac{kx_n}{g_1},t=t_0+\dfrac{kg_0}{g_1}\ (k\in \Bbb Z)\)。
其中 \(g_0s\) 为 \(g_0\) 倍数可以构造得出,\(x_nt\) 是 \(x_n\) 的倍数,这可以用 \(x_1=0,x_n\) 来构造。
事实上,只要 \(g_0s,x_nt\) 中有一个为偶数,我们就可以将 \(g\) 用类似 “\(2x-y\)” 的方式来表示。
显然若 \(\dfrac{kx_n}{g_1},\dfrac{kg_0}{g_1}\) 中有一个数为奇数则满足这个条件。考虑 \(g_1\) 必和 \(x_n,g_0\) 中一个数拥有相等的 \(2\) 的幂次。
如果 \(x_1\neq 0\) 呢?我们将每个数都减去 \(x_1\),这相当于在数轴上的平移,所以是等价的。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
typedef long long ll;
const int maxn=2e5+5;
int n;
ll k,a[maxn];
int main() {
rep(T,1,read(9)) {
n=read(9),k=read(9ll);
rep(i,1,n) a[i]=read(9ll);
ll g=a[2]-a[1];
/*
gcd(x_2-x_1,x_3-x_1,...,x_n-x_1)=gcd(x_2-x_1,x_3-x_2,...,x_n-x_{n-1})
可以用 gcd(a,b)=gcd(a-b,b) 证明
*/
rep(i,3,n)
g=gcd(g,a[i]-a[i-1]);
puts((k-a[1])%g?"NO":"YES");
}
return 0;
}
\(\text{E - Nezzar and Binary String}\)
解法
考虑反着做,因为有一个 “短浅” 的目标态。而由于每次改变量 严格小于 区间长度一半,我们只有一种选择方法。模拟即可。