( ext{C - Nezzar and Symmetric Array})
题目
简要题意
有一个长度为 (2n) 的数列 (a),其中每个数字都 互不相同,并且它还满足这样的条件:
- 对于任意 (1le ile 2n) 都存在一个 (j) 且 (1le jle 2n),有 (a_i=-a_j)。
但你不知道这个数列,你只知道一个长度为 (2n) 的数列 (d),有 (d_i=sum_{j=1}^{2n}|a_i-a_j|)。
数据范围与提示
有多组数据 (le 10^5),(1le nle 10^5, 0le d_ile 10^{12}),且 (sum nle 10^5)。
解法
#1. 一种方法
看到了 “某点与其它点的距离之和”,我们不禁将数轴画了出来。
显然相对原点对称的两点是等价的,而且要求数字 互不相同。那就首先无脑排个序,判断一下所有 (d) 值是否只出现两次,不是就是 no
了。
假设我们将 (a) 数列中 (>0) 的项拉出来再排个序,得到新的 (a) 数列,再把 (d) 数列中重复的项删掉。
初中老师告诉我们,(a_i) 对应着 (d_i),而且 (d_1) 值就是 (2 imessum_{i=1}^{n}a_i)。
对于 (d_{i},(i>1)),我们思考它与 (d_{i-1}) 之差代表了什么。显然对于 (1le 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 imes 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
";
}
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
";
} else {
if (last != 0 && last <= ((d[id] - 2 * sum) / num)) {
return "NO
";
}
last = ((d[id] - 2 * sum) / num);
sum += ((d[id] - 2 * sum) / num), id -= 2, num -= 2;
}
}
return "YES
";
}
int main() {
scanf("%lld", &T);
while (T--) {
cout << Solve();
}
return 0;
}
( ext{D - Nezzar and Board})
( ext{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} (kin 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 eq 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;
}
( ext{E - Nezzar and Binary String})
解法
考虑反着做,因为有一个 “短浅” 的目标态。而由于每次改变量 严格小于 区间长度一半,我们只有一种选择方法。模拟即可。