\(\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\)

所以

\[d_{n-1}=(n-1)\times 2a_{n-1}+2a_n \]

总结一下,有

\[a_i=\frac{d_i-\sum _{j=i+1}^n a_j}{2i} \]

这样可以逆序构造出 \(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}\)

解法

考虑反着做,因为有一个 “短浅” 的目标态。而由于每次改变量 严格小于 区间长度一半,我们只有一种选择方法。模拟即可。

posted on 2021-06-24 22:13  Oxide  阅读(38)  评论(0编辑  收藏  举报