Codeforces Round #698 (Div. 2)

比赛地址

A(水题)

题目链接

题目:
给出一个非严格单调递增的序列,分割成尽可能少的严格单调递增序列

解析:
由于所给序列是非严格单调递增的,所以假如对这个序列进行去重,那么就已经是一个严格单调递增的序列了,那么重复次数最多的元素,就决定了分割的最小数目

#include<bits/stdc++.h>
#define MEM(X,Y) memset(X,Y,sizeof(X))
typedef long long LL;
using namespace std;
/*===========================================*/

int vis[105];

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		MEM(vis, 0);
		int ret = 0;
		int n;
		scanf("%d", &n);
		int t;
		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &t);
			ret = max(ret, ++vis[t]);
		}
		printf("%d\n", ret);
	}
}

B(思维)

题目链接
⭐⭐

题目:
给出\(d(1\le d\le 9)\)\(k\),询问是否可以由若干个数中含有\(d\)的数累加获得\(k\)

解析:

  1. 首先可以确定,对于\(d0\sim d9\)这些数肯定可以得到,那么这些数加上\(nd\)一定可以获得之后的所有数
  2. 那现在考虑\(k< d0\)的情况,这个时候一定是由\(1d,2d,3d,\dots ,(d-1)d,n\times d\),累加而成,那么可以肯定\(k-n\times d(n\ge1)\)一定是\(10\)的倍数,且\(d\)不超过\(9\),保证了时间复杂度
#include<bits/stdc++.h>
using namespace std;

int main()
{
	int T;
	scanf("%d", &T);
	while (T--) {
		int q, t, d;
		scanf("%d%d", &q, &d);
		while (q--)
		{
			scanf("%d", &t);
			if (t >= d * 10)
				printf("YES\n");
			else
			{
				t -= d;
				while (t >= 0 && t % 10) t -= d;
				printf("%s\n", t < 0 ? "NO" : "YES");
			}
		}
	}

}

C(思维)

题目链接
⭐⭐⭐

题目:
给出一个长度为\(2n\)序列,问是否存在另一个序列\(dat\),使得每个元素\(d[i]=\sum_{j=1}^n|dat[i]-dat[j]|\),且若存在\(dat[i]\)必有\(dat[i]+dat[j]=0\)\(dat\)中每个元素互不相同

解析:

  1. 如果\(a>b\)那么\(|a-b|+|a+b|=2|a|\),反之\(|a-b|+|a+b|=2|b|\)
  2. 假如将原数组\(dat\)中的数按绝对值从小到大排序,对于排名为\(i_{th}\)\(dat[i]\),其对应的\(d[i]\)\(2\times(i\times dat[i]+dat[i+1]+\dots+dat[n])\),且其相反数对应的\(d[i]\)值应与之相同
  3. 那么就可以确定\(d\)中的每个数只能出现两次(在\(dat\)中的对应位置元素为某个绝对值对应的正值或负值),且必须均为偶数
  4. 并且由\(d[i]\)的公式可以知道,对于绝对值增大的序列,其对应的\(d\)也是逐渐增大的,那么可以使用优先队列递推求解出对应的原绝对值,如果出现小于等于0的或无法整除剩余个数时则判错
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
/*===========================================*/

const int maxn = 1e5 + 5;

set<long long, greater<long long>> s;
unordered_map<long long, int> m;
LL cnt, sum;

int main()
{
	int T;
	LL t;
	scanf("%d", &T);
	while (T--)
	{
		m.clear();
		s.clear();
		int n;
		bool ok = true;
		sum = cnt = 0;
		scanf("%d", &n);
		for (int i = 0; i < 2 * n; ++i)
		{
			scanf("%lld", &t);
			if (t & 1 || m[t] == 2) ok = false;
			if (!ok) continue;
			if (m[t])
				++cnt;
			else
				s.insert(t);
			++m[t];
		}
		if (cnt != n || !ok)
		{
			printf("NO\n");
			continue;
		}
		while (!s.empty())
		{
			t = *s.begin() / 2;
			s.erase(s.begin());
			t -= sum;
			if (t <= 0 || t % cnt)
			{
				ok = false;
				break;
			}
			sum += t / cnt;
			--cnt;
		}
		if (ok)
			printf("YES\n");
		else
			printf("NO\n");
	}
}

D(裴蜀定理)

题目链接
⭐⭐⭐⭐

题目:
给出一个数组,每次抽取两个数\(x,y\),将\(2x-y\)新增入数组,问这样的操作允许进行若干次,可否获得目标数\(k\)

解析:

  1. 假设\(x\)为任意\(dat\)中的元素,第一次进行操作\(2\times dat[i]-x\),第二次将这个数作为新的\(y\),又挑选新的\(dat[j]\)\(\underbrace{2\times(dat[j]-dat[i])}_p+x=k\),如果下括号这部分记为\(p\),显然\(p=2\times\sum_{i=1}^n\sum_{j=1}^n(k_{j,i}\times(dat[j]-dat[i]))\)
  2. 由裴蜀定理可以知道,\(p\)的结果一定为\(d=2\times gcd(dat[j]-dat[i])\{i,j|i,j\in n\}=2\times gcd(dat[i]-dat[i-1])\{i|i>1\}\)的倍数
  3. 这样可以获得所有能表式出的\(k\)

\[\begin{cases} x+nd=k &\text{Number of operations is even}\\ 2\times dat[j]-dat[i]+nd=k &\text{Number of operations is odd} \end{cases} \]

#include<bits/stdc++.h>
#define rep(i,a,b) for(int  i=a;i<b;++i)
#define rep1(i,a,b) for(int  i=a;i<=b;++i)
typedef long long LL;
using namespace std;
/*===========================================*/

template<typename T>
T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }

const int maxn = 2e5 + 5;
LL dat[maxn];


int main()
{
	int T;
	int n;
	LL k;
	scanf("%d", &T);
	while (T--)
	{
		LL d = 0;
		scanf("%d%lld", &n, &k);
		rep(i, 0, n)
			scanf("%lld", &dat[i]);
		rep(i, 1, n)
			d = gcd(abs(dat[i] - dat[i - 1]), d);
		d *= 2;
		k = (k % d + d) % d;
		bool ok = false;
		rep(i,0,n)
			if ((dat[i] % d + d) % d == k || ((2 * dat[1] - dat[i]) % d + d) % d == k)
			{
				ok = true;
				break;
			}
		printf("%s\n", ok ? "YES" : "NO");
	}
}
posted @ 2021-01-29 17:08  DreamW1ngs  阅读(57)  评论(0编辑  收藏  举报