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\)
解析:
- 首先可以确定,对于\(d0\sim d9\)这些数肯定可以得到,那么这些数加上\(nd\)一定可以获得之后的所有数
- 那现在考虑\(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\)中每个元素互不相同
解析:
- 如果\(a>b\)那么\(|a-b|+|a+b|=2|a|\),反之\(|a-b|+|a+b|=2|b|\)
- 假如将原数组\(dat\)中的数按绝对值从小到大排序,对于排名为\(i_{th}\)的\(dat[i]\),其对应的\(d[i]\)为\(2\times(i\times dat[i]+dat[i+1]+\dots+dat[n])\),且其相反数对应的\(d[i]\)值应与之相同
- 那么就可以确定\(d\)中的每个数只能出现两次(在\(dat\)中的对应位置元素为某个绝对值对应的正值或负值),且必须均为偶数
- 并且由\(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\)
解析:
- 假设\(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]))\)
- 由裴蜀定理可以知道,\(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\}\)的倍数
- 这样可以获得所有能表式出的\(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");
}
}
努力变成更好的自己吧!