11.22 CW 模拟赛 赛时记录
看题
也是把昨天的题调完 + 开大, 开局自带 \(15 \rm{min}\) 劣势(
还是老 \(\rm{Theme}\) 用的爽
机房有人能把薄膜键盘敲出机械的声音
感觉每场都只打了弱智分, 怎么办?
\(\rm{A}\)
我咧个逆序对啊
一眼找规律 + 思维, 应该能做
\(\rm{B}\)
像是图论一类的东西, 观察一下看能不能做
不是哥们, 不给部分分?
\(\rm{C}\)
我不到啊
\(\rm{D}\)
我不到啊
话说每次都看完题正序做, 好像也没什么意义
\(\rm{A}\)
容易的, 我们可以知道数列 \(a\) 的逆序对个数, 因为 \(n \leq 5000\) , 我们直接用 \(\mathcal{O} (n ^ 2)\) 算法即可
注意到 \(k \leq 10 ^ 6\) , 我们是有能力枚举每一个复制串的 (以下记为 \(p\) )
那么问题就简单了
考虑计算每个数在 \(a\) 数列中, 有多少个数比他大(以下设为 \(B_i\))
那么对于第 \(i\) 次重复, 前面就会有 \((i - 1) \times (\sum_{j = 1}^{n} B_j)\) 个自带逆序对, 然后对于这一次复制出来的数列 \(a\) , 再计算带来的逆序对即可
具体的, 枚举每一个复制串, 每一个复制串对答案的贡献为 \((i - 1) \times (\sum_{j = 1}^{n} B_j) + p\) , 总时间复杂度 \(\mathcal{O} (n ^ 2 + k)\)
\(\rm{B}\)
也是传统不写代码, 开就完了
显然的, 我们可以先转化问题
对于无向图上的 \(n\) 个点, 点之间的边权就是 \(\min(\text{图上的欧氏距离的平方和}, v)\) , 求走完所有点时经过的最小边权和
手玩样例看下有没有思路?
显然的, 对于 \(50 \rm{pts}\) , 状压可以解决
想到 \(1 \rm{h}\) 先跳过, 分数已经够多了
考虑剩下的 \(50 \rm{pts}\) , 注意到我们的目标是在完全图中掏一个长为 \(n\) 的环出来使得边权和最小, 显然可以状态压缩 , 由于是完全图, 状态膨胀非常快, 大概是 \(n^n\) 这一级的, 这一点也不好处理, 兴许有些妙妙做法可以做, 但我今天这状态应该止步于此了, \(\rm{lhs}\) 多半又 \(\rm{AK}\) 了
其实对于后面的点, 我有一个乱搞做法, 枚举起始点, 每个点贪心的选择最近点即可, 但是正确性比较的不显然, 冬天到了也是没有什么打的欲望
考虑剪掉不需要的枝, 然而剪不掉w
顺着状压 \(\rm{dp}\) 往下走
注意到每一条边 \((u, v)\) 都可以更新 \(State_{without \ v \ and \ with \ u} \to State_{with \ v \ and \ with \ u}\)
考虑逆向处理
我们可以知道, 对于每一个点 \(v\) , 我们可以枚举其出边权值最小, 加在一起即可
但是这里有约束条件, 注意到如果 \(u\) 已经不在集合中, 我们不能使用 \((u, v)\)
所以我们要找出 \(n - 1\) 条边, 其中每个点作为终点只出现一次
然后就可以用最小生成树处理了
\(\rm{C}\)
也是大劣势, 出场多半发现其他人都有 \(300 \rm{pts}\) 起手, 好嘛
神秘题目, 也是不太理解, 像 \(\rm{dp}\) 倒是
考虑 \(n \leq 13\) 的 \(40 \rm{pts}\) , 可以 \(\rm{dfs}\) 去处理, 对于每个点, 要么撤回到之前的点, 要么继续往前走, 要么留下来
有点困, 今天又要反向 \(\rm{rk} 1\) 了
稍微想拿多一点的分就需要想正解, 丢了
\(\rm{D}\)
高档暴力 \(60 \rm{pts}\) 可以打
对于 \(n = 3, 4, 5, 6\) , 直接枚举即可, 判断是简单的
对于 \(x = 1, y = 1, z = 1\) , 直接 \(n ^ 2\) 枚举分段即可
对于 \(x = 1, y = 1, z = 2\) , 考虑 \(n ^ 2\) 枚举的时候加点判断?
最后一段可以是
- 两个 \(1\)
- 一个 \(2\)
代码
也是留下接近 \(2\rm{h}\) 来打
\(\rm{A}\)
#include <bits/stdc++.h>
#define int long long
const int MAXN = 5200;
int n, k;
int a[MAXN];
class Sol_Class
{
private:
int p = 0;
int App[MAXN];
int SumB = 0;
int Ans = 0;
public:
/*计算数列 a 逆序对个数, 以及 B 的和*/
void CalcP()
{
memset(App, 0, sizeof(App));
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (a[j] > a[i]) p++;
}
App[a[i]]++;
}
// std::sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
for (int j = a[i] + 1; j <= 5000; j++)
SumB += App[j];
}
}
/*计算答案*/
void solve()
{
for (int i = 1; i <= k; i++) {
Ans += (i - 1) * SumB + p;
}
printf("%lld", Ans);
}
} Sol;
signed main()
{
#ifdef FILE_IO
freopen("inversion.in", "r", stdin);
freopen("inversion.out", "w", stdout);
#endif
scanf("%lld %lld", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
Sol.CalcP();
Sol.solve();
return 0;
}
虽然很神经, 但是打完了, 用时 \(16 \rm{min}\)
\(\rm{B}\)
建边跑 \(\rm{dp}\)
发现状压往里走应该就是正解, 那就把这个题放到最后去
\(\rm{C}\)
写个 \(\rm{dfs}\) 即可
喵了个咪的打不出来, 丢了
\(\rm{D}\)
#include <bits/stdc++.h>
const int MAXN = 70;
int n, x, y, z;
int s[MAXN];
bool check()
{
int Sum[MAXN];
memset(Sum, 0, sizeof(Sum));
for (int i = 1; i <= n; i++) Sum[i] = Sum[i - 1] + s[i];
int Cut1 = 0;
while(Sum[Cut1] < x) Cut1++;
if (Sum[Cut1] != x) return false;
int Cut2 = Cut1;
while(Sum[Cut2] - Sum[Cut1] < y) Cut2++;
if (Sum[Cut2] - Sum[Cut1] != y) return false;
if (Sum[n] - Sum[Cut2] != z) return false;
return true;
}
int Ans = 0;
void dfs(int Now)
{
if (Now == n) {
if (check())
Ans++;
return;
}
for (int i = 1; i <= 10; i++) {
s[Now + 1] = i;
dfs(Now + 1);
s[Now + 1] = 0;
}
}
int main()
{
#ifdef FILE_IO
freopen("subsequence.in", "r", stdin);
freopen("subsequence.out", "w", stdout);
#endif
scanf("%d %d %d %d", &n, &x, &y, &z);
if (n <= 6)
{
dfs(0);
printf("%d", Ans);
} else{
if(z == 1)
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n && i + j < n; j++) {
int k = n - i - j;
Ans += i * j * k;
}
}
else
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n && i + j < n; j++) {
int k = n - i - j;
if (k == 1) Ans += i * j * k;
else Ans += i * j * (k * (k - 1) / 2) + i * j * k;
}
}
printf("%d", Ans);
}
return 0;
}
欧呦, 谢天谢地了, \(30 \rm{min}\) 还要想 \(\rm{B}\) , 从某种意义上来讲, 我是备战明天比赛的第一个 \(\rm{Man}\)

浙公网安备 33010602011771号