Educational Codeforces Round 141

写在前面

比赛地址:https://codeforces.com/contest/1783

CF 车队翻车咯,本来上大分,喜提 skipped

A

如果所有数均相等则无解。

否则先降序排序,交替输出 aiani+1 即可。

复制复制
//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
//=============================================================
int a[1100];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
int T = read();
while (T --) {
int n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
std::sort(a + 1, a + n + 1);
if (a[n] == a[1]) {
printf("NO\n");
continue;
}
printf("YES\n");
for (int i = 1, j = n; i <= j; ++ i, -- j) {
if (i == j) printf("%d ", a[i]);
else printf("%d %d ", a[j], a[i]);
}
printf("\n");
}
return 0;
}

B

尝试达到上界。

蛇形矩阵,交替输出 ini+1 即可。

//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
//=============================================================
int a[51 * 51], b[51][51];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
int n = read(), p = 0;
for (int i = 1, j = n * n; i <= j; ++ i, -- j) {
a[++ p] = i, a[++ p] = j;
if (i == j) -- p;
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
b[i][j] = 0;
}
}
b[1][1] = a[1];
for (int i = 1, j = 1, tot = 1; tot < n * n; ) {
while(++ j <= n && !b[i][j]) b[i][j] = a[++ tot]; -- j;
while(++ i <= n && !b[i][j]) b[i][j] = a[++ tot]; -- i;
while(-- j > 0 && !b[i][j]) b[i][j] = a[++ tot]; ++ j;
while(-- i > 0 && !b[i][j]) b[i][j] = a[++ tot]; ++ i;
}
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= n; ++ j) {
printf("%d ", b[i][j]);
}
printf("\n");
}
}
return 0;
}

C

显然获胜场数越多越好,最后的胜利场数一定是最大值,先贪心地求出这个最大值 m

再考虑其他人的获胜场数,仅需关注有多少人获胜场数大于 m 即可。编号 im 的人获胜场数一定不大于 m,编号 i>m+1 的人获胜场数一定大于 m,是否选择战胜他们并不会影响排名。仅需考虑是否战胜了编号 m+1 的人,回退贪心时选择的代价最大的元素并尝试选择 am+1 即可。

赛时没考虑清楚写的相当麻烦。

//By:Luckyblock
/*
*/
#include <vector>
#include <cstdio>
#include <cctype>
#include <algorithm>
const int kN = 5e5 + 10;
//=============================================================
int n, m, a[kN], b[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
n = read(), m = read();
int sum = 0;
for (int i = 1; i <= n; ++ i) a[i] = b[i] = read();
std::sort(b + 1, b + n + 1);
for (int i = 1; i <= n; ++ i) {
if (b[i] > m) break;
m -= b[i];
sum ++;
}
if (sum == n) {
printf("1\n");
continue;
}
printf("%d\n",
m + b[sum] - a[sum + 1] >= 0 ? n - sum: n - sum + 1);
}
return 0;
}

D

给定一长度为 n 的数列 a,要求进行 n2 次操作,第 i 次操作有两种选择:

  • ai=ai+ai+1ai+2=ai+2ai+1
  • ai=aiai+1ai+2=ai+2+ai+1

求完成所有操作后,数列 a 的形态的种类数。
3n3000ai300
2S,512MB。

i 次操作后,a1ai+1 的形态有 fi 种,考虑第 i+1 次操作的影响。发现并不会影响 a1ai1ai+3an 的值,它们的形态并不会影响本次操作对 a1ai+2 形态种类数,我们仅需关注 ai,ai+1ai+2 的值即可。更近一步地,由于 ai 的值在操作前已经确定,ai+1 的值在操作中不会改变,考虑枚举 ai+1 的值,我们仅需考虑 ai+2 的值即可。

fi,k 表示进行到第 i 次操作,ai+2=ka 的形态数。转移时考虑枚举 ai+1 的值 j

  • j=ai+1=0,两种操作等价,有:

    fi,ai+2=fi1,0

  • j=ai+10,则有:

    fi,k=fi1,a2j+fi1,a2+j(k=a2jk=a2+j)

答案即 kfn2,k

总复杂度 O(n×A) 级别,A 为值域。

注意 |A|300×300ai 可能为负数,注意添加增量避免数组下标为 0。

//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
const int mod = 998244353;
const int kN = 3e2 + 10;
const int kM = 3e5;
const int zero = 1e5;
//=============================================================
int n, a[kN], ans, f[kN][kM];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
f[0][a[2] + zero] = 1;
for (int i = 1; i <= n - 2; ++ i) {
int val = a[i + 2];
for (int j = 0; j < kM; ++ j) {
int nval1 = val + j, nval2 = val - (j - zero) + zero;
f[i][nval1] = (f[i][nval1] + f[i - 1][j]) % mod;
if (j != zero) f[i][nval2] = (f[i][nval2] + f[i - 1][j]) % mod;
}
}
for (int i = 0; i < kM; ++ i) ans = (ans + f[n - 2][i]) % mod;
printf("%lld\n", ans);
return 0;
}

E

AB 两人在玩一个游戏,游戏中共有 n 个怪物。对于每一个怪物,A,B 可以轮流攻击 k 次,A 先出手,两人的攻击互不影响。A 的第 ai 次攻击才能击败第 i 个怪物,B 的第 bi 次攻击才能击败第 i 个怪物。求所有的整数 k[1,n],使得所有怪物均是被 A 击败的。
t 组数据,每组数据给定两长度为 n 的数组 a,b,代表上述游戏的参数,求所有的整数 k
1t1041n2×1051ai,binn2×105
2S,256MB。

没做亏死……虽然就算做了也会翻车,乐

对于一个固定的 k,第 i 个怪物被 A 击败的条件为:aikbik,考虑反面,被 B 击败的条件为 aik>bik,则 (bik,aik) 之间至少有一个整数,则有 bi<ai,且 [bi,ai) 之间至少有一个 k 的倍数。

考虑枚举 k,检查 k 的倍数是否位于某个区间 [bi,ai1] 中即可判断是否有解。差分标记区间即可。

复杂度是调和级数,O(nlogn) 级别。

//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN], b[kN], d[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
int T = read();
while (T --) {
n = read();
for (int i = 1; i <= n; ++ i) d[i] = 0;
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int i = 1; i <= n; ++ i) b[i] = read();
for (int i = 1; i <= n; ++ i) {
if (b[i] <= a[i]) {
++ d[b[i]], -- d[a[i]];
}
}
for (int i = 1; i <= n; ++ i) d[i] += d[i - 1];
std::vector <int> ans;
for (int k = 1; k <= n; ++ k) {
bool flag = 1;
for (int i = 1; i * k <= n; ++ i) {
if (d[i * k]) {
flag = 0;
break;
}
}
if (flag) ans.push_back(k);
}
printf("%d\n", ans.size());
for (int i = 0, sz = ans.size(); i < sz; ++ i) printf("%d ", ans[i]);
printf("\n");
}
return 0;
}

F

给定两个长度为 n 的排列 a,b,可以进行任意次操作。每次操作可选择整数 i[1,n],设 x,y 分别满足 ax=i,by=i,交换 aiaxbi,by
要求构造一种操作数 2n 的方案,使得使两个排列均有序,且最小化操作次数。
2n30001ai,bin

写了个贪心调了一天没调出来最后发现假了,心力交瘁,改天再说、、、

首先,显然至多进行 n1 次操作,因为每次操作都至少会让两个排列中的某个数交换到有序位置。

考虑排列元素交换的套路(如 CF1768D),对于一个排列,建图令 iai/bi,使图中每一个连通块有序的代价为块的大小 -1,即每个连通块中都有一个不需要操作的 i,其他的 i 都需要被操作。总代价为点数-连通块数。

再考虑两个排列的情况,记两个排列对应的图分别为 A,B。考虑问题反面,最大化不需要被操作的位置数。放到图上看,如果一个位置 i 可以不被操作,那么 A,Bi 所在的连通块中的其他位置都需要被操作。

考虑对这个问题再建立图论模型,记 A,Bi 所在的连通块分别为 Ai,Bi,令 AiBi,可以发现这是一张二分图,每个原排列中的位置对应二分图的一条边。最大化不需要被操作的位置数,等价于求该二分图的最大匹配数,最小操作次数即 n 最大匹配数。

注意二分图中可能有重边,而最大匹配时仅会选择重边中的一条,输出方案时应注意判断。

总复杂度 O(n2) 级别。

//By:Luckyblock
/*
*/
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
const int kN = 3e3 + 10;
const int kM = kN;
//=============================================================
int n, ans, a[kN], b[kN];
int numa, numb, bela[kN], belb[kN];
bool visa[kN], visb[kN];
int edgenum, head[kN], v[kM], ne[kM];
int match[kN];
bool vis[kN], visans[kN];
//=============================================================
inline int read() {
int f = 1, w = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0';
return f * w;
}
void Dfsa(int u_) {
if (visa[u_]) return ;
visa[u_] = 1, bela[u_] = numa;
Dfsa(a[u_]);
}
void Dfsb(int u_) {
if (visb[u_]) return ;
visb[u_] = 1, belb[u_] = numb;
Dfsb(b[u_]);
}
void Add(int u_, int v_) {
v[++ edgenum] = v_;
ne[edgenum] = head[u_];
head[u_] = edgenum;
}
bool Dfs(int u_) {
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (vis[v_]) continue;
vis[v_] = 1;
if (!match[v_] || Dfs(match[v_])) {
match[v_] = u_;
return 1;
}
}
return 0;
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
n = read();
for (int i = 1; i <= n; ++ i) a[i] = read();
for (int j = 1; j <= n; ++ j) b[j] = read();
for (int i = 1; i <= n; ++ i) {
if (!visa[i]) ++ numa, Dfsa(i);
if (!visb[i]) ++ numb, Dfsb(i);
}
for (int i = 1; i <= n; ++ i)
Add(bela[i], belb[i]);
for (int i = 1; i <= numa; ++ i) {
for (int j = 1; j <= numb; ++ j) vis[j] = 0;
if (Dfs(i)) ++ ans;
}
printf("%d\n", n - ans);
for (int i = 1; i <= numa; ++ i) {
for (int j = head[i]; j; j = ne[j]) {
if (match[v[j]] == i) {
visans[j] = 1;
match[v[j]] = 0;
}
}
}
for (int i = 1; i <= n; ++ i) {
if (!visans[i]) printf("%d ", i);
}
return 0;
}

G

很有意思的线段树分治。

但是这个分析太过牛逼,虽然看懂了但不太明白思路怎么来的。

牛逼。

先不写了就,什么时候复习线段树分治拉出来写一下。

写在最后

  • 开车需谨慎。
  • 想清楚再写,别急。
  • 考虑影响因素,影响因素相对较少且无后效性考虑 dp。
  • 考虑反面。
  • 模型转换。
  • 仁王真好玩,在线求斧哥送我仁王 2 玩。
posted @   Luckyblock  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示