2024.09.14模拟赛总结
$ T1 $
似乎是签到题,但是没开 $ unsigned $ $ long $ $ long $ 挂成 $ 88 $ 分了。
直接模拟即可,从后往前考虑,将每个数放到离其最近的位置,不过不会证...
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int N = 1000010;
struct wasd
{
int ct, v;
} unq[N];
int n, a[N], b[N];
LL res;
map<int, bool> m;
map<int, int> ctn;
vector<LL> ans;
bool cmp(LL a, LL b)
{
return a > b;
}
int main()
{
freopen("openhook.in", "r", stdin);
freopen("openhook.out", "w", stdout);
// cout << mod << '\n';
scanf("%lld", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", a + i);
for (int i = 1; i <= n; i ++ ) scanf("%d", b + i);
sort(b + 1, b + n + 1);
sort(a + 1, a + 1 + n);
int cnt = 0;
for (int i = 1; i <= n; i ++ )
{
if (m.count(a[i]))
{
unq[cnt].ct ++ ;
continue;
}
unq[ ++ cnt].v = a[i];
unq[cnt].ct = 1;
m[a[i]] = 1;
}
int lst_pos = unq[cnt].v + 1;
for (int j = 1; j < unq[cnt].ct; j ++ )
ans.push_back(lst_pos - unq[cnt].v), lst_pos ++ ;
for (int i = cnt; i > 1; i -- )
{
ctn[unq[i].v] = lst_pos;
// cout << unq[i].v << ' ' << lst_pos << "wasn\n";
lst_pos = unq[i - 1].v + 1;
while (ctn.count(lst_pos)) lst_pos = ctn[lst_pos];
for (int j = 1; j < unq[i - 1].ct; j ++ )
{
// cout << unq[i - 1].v << ' ' << lst_pos << '\n';
ans.push_back(lst_pos - unq[i - 1].v);
lst_pos ++ ;
while (ctn.count(lst_pos)) lst_pos = ctn[lst_pos];
}
}
int ct = 0;
sort(ans.begin(), ans.end(), cmp);
for (auto x : ans) res += x * (LL)b[ ++ ct];
cout << res << '\n';
return 0;
}
/*
10
1 1 1 1 3 5 5 6 7 7
1919810 1919810 1919810 1919810 1919810 1919810 1919810 1919810 1919810 1919810
1 4
3 1
5 2
6 1
7 2
1 + 3 + 1 + 3 + 9
*/
$ T2 $
赛时基本上没有想,毕竟有关 $ mex $ 的题做得还是太少了。
只想到了一个 $ O(n ^ 3) $ 的做法:设 $ f[i][mex] $ 表示前 $ i $ 个数划分时,每段 $ mex $ 为 $ mex $ 的方案数。
大概是因为没有发现性质。
注意到一个性质:
整个数组的 $ mex $ 应当是要等于每个划分段的 $ mex $ 的。
设划分段 $ mex $ 都为 $ X $,整个数组的 $ mex $ 为 $ Y $。
那么 $ 0 \sim Y - 1 $ 应该都在数组中出现过了。
所以可以得出 $ X \ge Y $,再考虑 $ X > Y $ 的情况。
因为整个数组的 $ mex $ 为 $ Y $,那么 $ Y $ 一定没有出现过,所以 $ X $ 只能是 $ Y $。
得到了这个性质,不难想到 DP。
设 $ f_i $ 表示将数组前 $ i $ 位划分的方案数。
状态转移方程即为
$ f_i = \sum_{j = 1}^{i - 1} f_{j - 1} (mex[j, i] = Y)$
然后发现在这个过程中,$ j $ 只会向右移,于是可以用双指针 $ + $ 前缀和优化,是时间复杂度降到 $ O(n) $。
由于是前缀和,所以答案为 $ f_n - f_{n - 1} $。
#include <bits/stdc++.h>
#define lbw 37000000
#define thr 300010
using namespace std;
const int N = 37000010, mod = 1e9 + 7;
int n, a[N], cnt[N];
int f[N], mex;
signed main()
{
freopen("clods.in", "r", stdin);
freopen("clods.out", "w", stdout);
int T;
cin >> T;
while (T -- )
{
cin >> n;
if (n != lbw)
for (int i = 1; i <= n; i ++ ) cin >> a[i];
else
{
int x, y;
cin >> x >> y;
a[1] = 0;
for (int i = 2; i <= n; i ++ )
a[i] = (a[i - 1] * x + y + i) & 262143;
}
f[0] = 1;
for (int i = 0; i <= min(thr, n); i ++ ) cnt[i] = 0;
for (int i = 1; i <= n; i ++ )
cnt[a[i]] ++ ;
mex = 0;
while (cnt[mex] > 0) mex ++ ;
for (int i = 0; i <= min(thr, n); i ++ ) cnt[i] = 0;
int nw = 0, pre = -1;
for (int i = 1, j = 0; i <= n; i ++ )
{
pre = -1;
cnt[a[i]] ++ ;
while (cnt[nw] > 0) nw ++ ;
while (nw >= mex && j <= i)
{
j = max(j, 1);
cnt[a[j]] -- ;
if (!cnt[a[j]] && a[j] < nw) pre = nw, nw = a[j];
j ++ ;
}
if (~pre)
{
j -- ;
cnt[a[j]] ++ ;
nw = pre;
}
f[i] = f[i - 1];
if (j > 0) (f[i] += f[j - 1]) %= mod;
}
cout << (f[n] - f[n - 1] + mod) % mod << '\n';
}
return 0;
}
$ T3 $
赛时本来想写暴力的,但是没写对。
rfy 似乎之前讲过,但是听完忘了,也没做。
做法是 Kruskal 重构树,每条边以相连的点较小的编号为边权,建出 Kruskal 重构树。
然后对于题目中的要求就可以转化为点权为 $ x $ 的点,需是编号为 $ y $ 的点的祖先。
对于第二个条件,将上面建第一棵树的方法反一下即可,也可以转化为点权为 $ y $ 的点,需是编号为 $ x $ 的点的祖先。
然后考虑如何统计满足条件的 $ [x, y] $ 的对数。
一种常用技巧是先求出第二棵树的 $ dfn $ 序,然后在遍历第一棵树到一个非叶子结点时,在树状数组上加一,最后离开时减去即可,统计就是树状数组求和,一个节点的子树范围为 $ dfn_u \sim dfn_u + siz_u - 1 $。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 4000010, M = N;
LL tr[N];
int n;
LL res;
int mp[N];
int h1[N], e1[M], ne1[M], idx1;
int h2[N], e2[M], ne2[M], idx2;
int dfn[N], time_stamp, siz[N];
struct Edge
{
int a, b, c;
} edge[N], edge1[N];
int p[N], p1[N], w[N], w1[N];
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}
int find1(int x)
{
return p1[x] == x ? x : p1[x] = find1(p1[x]);
}
int lowbit(int x)
{
return x & -x;
}
void update(int x, int v)
{
for (int i = x; i <= 2 * n - 1; i += lowbit(i)) tr[i] += v;
}
LL query(int x)
{
LL res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void add1(int a, int b)
{
e1[idx1] = b, ne1[idx1] = h1[a], h1[a] = idx1 ++ ;
}
void add2(int a, int b)
{
e2[idx2] = b, ne2[idx2] = h2[a], h2[a] = idx2 ++ ;
}
bool cmp(Edge a, Edge b)
{
return a.c > b.c;
}
bool cmp1(Edge a, Edge b)
{
return a.c < b.c;
}
void dfs1(int u, int fa)
{
dfn[u] = ++ time_stamp;
siz[u] = 1;
for (int i = h2[u]; ~i; i = ne2[i])
{
int j = e2[i];
if (j == fa) continue;
dfs1(j, u);
siz[u] += siz[j];
}
}
void dfs2(int u, int fa)
{
bool fuck = 1;
for (int i = h1[u]; ~i; i = ne1[i])
{
int j = e1[i];
if (j == fa) continue;
fuck = 0;
}
if (!fuck)
{
if (!mp[dfn[w[u]]]) update(dfn[w[u]], 1);
mp[dfn[w[u]]] ++ ;
}
for (int i = h1[u]; ~i; i = ne1[i])
{
int j = e1[i];
if (j == fa) continue;
dfs2(j, u);
}
if (fuck) res += query(dfn[w1[u]] + siz[w1[u]] - 1) - query(dfn[w1[u]] - 1) - query(dfn[u]) + query(dfn[u] - 1);
if (!fuck)
{
if (mp[dfn[w[u]]] == 1) update(dfn[w[u]], -1);
mp[dfn[w[u]]] -- ;
}
}
signed main()
{
freopen("charity.in", "r", stdin);
freopen("charity.out", "w", stdout);
memset(h1, -1, sizeof h1);
memset(h2, -1, sizeof h2);
scanf("%d", &n);
for (int i = 1; i <= 2 * n - 1; i ++ ) p[i] = p1[i] = i;
for (int i = 1; i <= n; i ++ )
{
int x;
scanf("%d", &x);
if (!x) continue;
edge[i - 1] = {i, x, min(i, x)};
edge1[i - 1] = {i, x, max(i, x)};
}
sort(edge + 1, edge + n, cmp);
sort(edge1 + 1, edge1 + n, cmp1);
int cnt = n;
for (int i = 1; i <= n; i ++ ) w[i] = i;
for (int i = 1; i < n; i ++ )
{
int x = edge[i].a, y = edge[i].b;
x = find(x), y = find(y);
p[x] = p[y] = ++ cnt;
w[cnt] = edge[i].c;
add1(cnt, x), add1(cnt, y);
}
cnt = n;
for (int i = 1; i <= n; i ++ ) w1[i] = i;
for (int i = 1; i < n; i ++ )
{
int x = edge1[i].a, y = edge1[i].b;
x = find1(x), y = find1(y);
p1[x] = p1[y] = ++ cnt;
w1[edge1[i].c] = cnt;
add2(cnt, x), add2(cnt, y);
}
dfs1(2 * n - 1, -1);
dfs2(2 * n - 1, -1);
cout << res << '\n';
return 0;
}
$ 2024.09.15 $ 初赛模拟
$ 69 $ 分
感觉有点超纲,考到了网络流、虚树什么的。
前面单项选择的话,错的有点多,特别是有关期望、组合计数的题目,基本上是错的。
然后阅读程序,没看得很懂,错的也比较多。
最后是完善程序,感觉难度正常,毕竟给出了解题思路,而且有很多地方都有提示到,只错了一题。
本文来自博客园,作者:爱朝比奈まふゆ的MafuyuQWQ。 转载请注明原文链接:https://www.cnblogs.com/MafuyuQWQ/p/18413789

浙公网安备 33010602011771号