Codeforces Round #698 (Div. 2)

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

所以

[d_{n-1}=(n-1) imes 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
";
  }
  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})

解法

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

原文地址:https://www.cnblogs.com/AWhiteWall/p/14928815.html