A. Meximization
思路
对数组排序,并且把重复的数组放到最后,这样就可以保证对于任意的前 i 个元素的MEX最大。
AC代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;
using namespace std;
int a[maxn];
int v[105];
vector<int> A;
vector<int> B;
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a+1, a+1+n);
for(int i = 1; i <= n; i++){
if(v[a[i]] == 0){
A.push_back(a[i]);
v[a[i]] = 1;
}
else B.push_back(a[i]);
}
for(int i = 0; i < A.size(); i++) cout << A[i] << " ";
for(int i = 0; i < B.size(); i++) cout << B[i] << " ";
cout << endl;
A.clear(); B.clear(); chushi(v, 0);
}
return 0;
}
B. M-arrays
思路
使每个数对 m 取模,并且计数。让余数为 i 和 m-i 的放到同一个组里边,并且用 ABABA 这样的形式排列。特殊处理余数为 0 和 m/2 的数。
AC代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;
using namespace std;
int v[maxn];
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n, m;
cin >> n >> m;
int num;
for(int i = 1; i <= n; i++){
cin >> num;
v[num % m]++;
}
int res = 0;
if(v[0] != 0) res++;
if(m % 2 == 0 && v[m/2] != 0) res++;
for(int i = 1; i < m - i; i++){
int tmp = min(v[i], v[m-i]);
if(tmp == 0){
res += v[i];
res += v[m-i];
}
else{
res++;
if(v[i] == v[m-i]) continue;
else if(v[i] + 1 == v[m-i]) continue;
else if(v[i] == v[m-i] + 1) continue;
else res += max(v[i], v[m-i]) - tmp - 1;
}
}
cout << res << endl;
chushi(v, 0);
}
return 0;
}
C. k-LCM
思路
当成构造题写就简单了,当数论题想不明白。
在 C1 中,把 k 固定成了三个数。
也就是说,把 n 分成三个数,使得这三个数的最小公倍数小于等于 n 的一半就行了。
对于一个数 x,把它分成 x/2 和 x/2。那么 LCM(x/2, x/2) = x/2,。
由 👆 可知,如果 x 是奇数,我们可以分成 1, (x-1) / 2, (x-1) / 2。
再来讨论 x 是偶数的时候:
类比刚刚的写法,和容易想到的是 2, (x-2) / 2,(x-2) / 2。
不过我们注意一个问题,1 对另外两个数的 LCM 是没有影响的。但 2 会影响两个奇数的 LCM。
eg: LCM(5, 5) = 5。 LCM(1,5,5) = 5。LCM(2,5,5) = 10。
所以,如果 (x-2) / 2 是偶数,我们可以把 x 分成 2, (x-2) / 2,(x-2) / 2。
当 (x-2) / 2 是奇数时,可以推出 x / 2 是偶数, 也就是说我们可以把 x 分成 x / 2,x / 4,x / 4。
再来说说C2,这里的 k 不一定为 3。
刚刚说到一个点,1 对两个数的 LCM 是没有影响的。
我们可以把 n 分成 k-3 个 1 和 三个数。然后再套 C1 的结论就好了。
AC代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;
using namespace std;
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n, k;
cin >> n >> k;
if(k > 3) for(int i = 4; i <= k; i++) cout << "1 ";
n = n - k + 3;
if(n % 2 == 1) cout << "1 " << (n-1)/2 << " " << (n-1)/2 << endl;
else{
int tmp = n / 2;
if(tmp % 2 == 1) cout << "2 " << (n-2)/2 << " " << (n-2)/2 << endl;
else cout << tmp << " " << tmp/2 << " " << tmp/2 << endl;
}
}
return 0;
}
D. Genius
思路
这个题给的内存很小,要注意这个问题。
根据题意我们就可以知道,IQ 是在不断变大的。
我们先写出来各个题目之间的 IQ 变化:
W(2, 4) = 2 | W(2, 8) = 6 | W(2, 16) = 14 | W(2, 32) = 30 | W(2, 64) = 62 |
---|---|---|---|---|
W(4, 8) = 4 | W(4, 16) = 12 | W(4, 32) = 28 | W(4, 64) = 60 | |
W(8, 16) = 8 | W(8, 32) = 24 | W(8, 64) = 56 | ||
W(16, 32) = 16 | W(16, 64) = 48 | |||
W(32, 64) = 32 |
根据贪心,我们统计出从低IQ 走到 高IQ,对应每一题的最大分数。
AC 代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;
using namespace std;
ll tag[maxn];
ll s[maxn];
ll dp[maxn];
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> tag[i];
for(int i = 1; i <= n; i++) cin >> s[i];
for(int i = 1; i <= n; i++) dp[i] = 0;
for(int i = 1; i <= n; i++){
for(int j = i-1; j >= 1; j--){
if(tag[i] == tag[j]) continue;
ll w = labs(s[i] - s[j]);
ll num1 = dp[i], num2 = dp[j];
dp[i] = max(dp[i], num2 + w);
dp[j] = max(dp[j], num1 + w);
}
}
ll res = -1;
for(int i = 1; i <= n; i++) res = max(res, dp[i]);
cout << res << endl;
}
return 0;
}
E. Square-free division
思路
先来说说唯一分解定理:
对于任何一个数 x,都有存在 x = 2a1 + 3 a2 + 5 a3 + ……
应用这个定理我们可以知道,如果 x * y 是一个完全平方数,那么:
x 对应的 a1,a2,a3,…… 与 y 对应的 b1,b2,b3,…… ,一定满足 a1 + b1 是偶数,a2 + b2 是偶数,a3 + b3 是偶数,……
由👆可以知,E1 中我们依次寻找满足不满住上述条件的最长区间即可,区间数就是答案。
乱搞代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e5 + 5;
using namespace std;
set<set<int> > V;
int main(){
int ncase;
cin >> ncase;
while(ncase--){
int n, k;
cin >> n >> k;
int res = 0;
for(int i = 1; i <= n; i++){
int x;
cin >> x;
set<int> S;
S.insert(1);
for(int j = 2; j * j <= x; j++){
int tot = 0;
while(x % j == 0){
tot++;
x /= j;
}
if(tot & 1) S.insert(j);
}
if(x > 1) S.insert(x);
if(V.count(S)){
res++;
V.clear();
}
V.insert(S);
}
if(!V.empty()) res++;
V.clear();
cout << res << endl;
}
return 0;
}
对于 E2,可以进行 k 次操作,每次操作可以把一个数改成任意的数。
这里我们就要进行区间DP。
先统计出 l[k][i] ,对于第 i 个数,在修改 k 次的情况下,能达到最远的左端点。
在修改 k+j 次的情况下,到第 i 个数,划分的最小区间个数 dp[k+j][i] = min(dp[k+j][i], dp[j][l[k][i]-1]+1)。
AC代码
#include <bits/stdc++.h>
#include <vector>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <stack>
#define ll long long
#define chushi(a, b) memset(a, b, sizeof(a))
#define endl "\n"
const double eps = 1e-8;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int maxn = 1e7 + 5;
using namespace std;
template<class T>inline void read(T &res)
{
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
int v[maxn] = {0};
int a[maxn];
int get(int x){
int res = 1;
for(int i = 2; i * i <= x; i++){
if(x % i == 0){
int tot = 0;
while(x % i == 0){
x /= i;
tot++;
}
if(tot & 1) res *= i;
}
}
res *= x;
return res;
}
int suf = 0;
void add(int x, int w){
suf -= max(0, v[x]-1); // 先减掉这个数对 suf 的影响
v[x] += w; // 修改这个数出现的次数
suf += max(0, v[x]-1); // 再加上修改过后对 suf 的影响
}
int l[25][200005];
int dp[25][200005];
int main(){
int ncase;
read(ncase);
while(ncase--){
int n, m;
read(n); read(m);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= n; i++) a[i] = get(a[i]);
for(int k = 0; k <= m; k++){
int s = 1; suf = 0; // suf表示从 s 开始到 i 需要修改的数的个数
for(int i = 1; i <= n; i++) v[a[i]] = 0;
for(int i = 1; i <= n; i++){
add(a[i], 1);
while(s <= i && suf > k){ // 需要修改的数超过 k 时,就要开始修改左端点 s
add(a[s], -1);
s++;
}
l[k][i] = s; // 对于第 i 个数,在修改 k 次的情况下,能达到最远的左端点
}
}
for(int k = 0; k <= m; k++){ // 初始化
for(int i = 0; i <= n; i++){
dp[k][i] = 1e9;
}
}
dp[0][0] = 0; // 初始化
for(int i = 1; i <= n; i++){ // 区间DP,
for(int k = 0; k <= m; k++){
for(int j = 0; j <= m; j++){
if(k+j > m) break; // 在修改 k+j 次的情况下,到第 i 个数,划分的最小区间个数
dp[k+j][i] = min(dp[k+j][i], dp[j][l[k][i]-1]+1);
}
}
}
int res = 1e9;
for(int k = 0; k <= m; k++) res = min(res, dp[k][n]);
printf("%d\n", res);
}
return 0;
}