[赛记] 多校A层冲刺NOIP2024模拟赛09 && 10
排列最小生成树 (pmst) 50pts
又是诈骗题,然后又不会。。。
暴力很暴力,直接建个完全图跑 Kruskal
即可;
正解考虑如果我们连接编号相邻的点,那么每个边的边权都小于 真能考虑到吗?;
所以我们最终的最小生成树中的边边权都小于
那么对于
所以我们在连边的时候只需将原数组按编号和
那么总边数为 Kruskal
过程中需要排序,如果 sort
的话会带
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n;
int b[500005], a[500005];
struct sas{
unsigned short f, t, w;
bool operator <(const sas &A) const {
return w < A.w;
}
}e[23000005];
long long len(int x, int y) {
return 1ll * abs(x - y) * abs(b[x] - b[y]);
}
int cnt;
void add(int u, int v, unsigned short ww) {
e[++cnt].f = u;
e[cnt].t = v;
e[cnt].w = ww;
}
int fa[500005];
int find(int x) {
if (x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
long long Kru() {
long long ans = 0;
sort(e + 1, e + 1 + cnt);
int sum = 0;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= cnt; i++) {
if (sum == n - 1) break;
int x = find(e[i].f);
int y = find(e[i].t);
if (x != y) {
fa[x] = y;
ans += 1ll * e[i].w;
}
}
return ans;
}
int main() {
freopen("pmst.in", "r", stdin);
freopen("pmst.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> b[i];
a[b[i]] = i;
}
int sq = sqrt(n);
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= min(n, i + 1 + sq); j++) {
if (len(i, j) <= n) add(i, j, len(i, j));
if (len(a[i], a[j]) <= n) add(a[i], a[j], len(a[i], a[j]));
}
}
cout << Kru();
return 0;
}
卡牌游戏 (cardgame) 100pts
签到题居然在T2。。。;
看到特殊性质有
这里我们默认
手模一下,可以发现,对于互质的情况,
对于不互质的情况,我们可以发现,
于是我们维护一下从 1 到 gcd(n, m) 开始,每次下标加 gcd(n, m) 的数组,然后同时维护一下
查询直接二分查找;
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
long long n, m;
long long a[500005], b[500005];
long long cnt[100005];
vector<long long> v[100005];
long long gc;
long long lose, win, he;
bool vis;
int main() {
freopen("cardgame.in", "r", stdin);
freopen("cardgame.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
cin >> b[i];
}
if (n > m) {
vis = true;
for (int i = 1; i <= n; i++) swap(a[i], b[i]);
swap(n, m);
}
gc = __gcd(n, m);
for (int i = 1; i <= gc; i++) {
for (int j = i; j <= m; j += gc) {
v[i].push_back(b[j]);
cnt[i]++;
}
sort(v[i].begin(), v[i].end());
}
for (int i = 1; i <= n; i++) {
long long now = i % gc;
if (now == 0) now = gc;
long long val = (n * m) / (n * cnt[now]);
long long lpos = lower_bound(v[now].begin(), v[now].end(), a[i]) - v[now].begin();
win += lpos * val;
long long rpos = upper_bound(v[now].begin(), v[now].end(), a[i]) - v[now].begin();
if (lpos < v[now].size() && v[now][lpos] == a[i]) {
he += (rpos - lpos) * val;
}
if (rpos < v[now].size()) {
lose += (v[now].size() - rpos) * val;
}
}
if (vis) {
cout << lose << '\n' << win << '\n' << he;
} else {
cout << win << '\n' << lose << '\n' << he;
}
return 0;
}
比特跳跃 (jump) 39pts
考虑正解,对于这个题,其实有三个子问题;
;
首先我们将
;
对于
;
对于
所以我们先将
注意这里的更新有
上面三个子问题都连了
点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int n, m, s;
long long k;
long long p[25];
struct sss{
int t, ne;
long long w;
}e[5000005];
int h[5000005], cnt;
void add(int u, int v, long long ww) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
e[cnt].w = ww;
}
long long dis[500005], f[500005];
bool vi[5000005];
bool vis[500005];
void dij(int x, bool is) {
priority_queue<pair<long long, int>, vector<pair<long long, int> >, greater<pair<long long, int> > > q;
if (is) {
memset(dis, 0x3f, sizeof(dis));
} else {
for (int i = 2; i <= n; i++) q.push({dis[i], i});
}
memset(vis, false, sizeof(vis));
q.push({0, x});
dis[x] = 0;
while(!q.empty()) {
int xu = q.top().second;
q.pop();
if (vis[xu]) continue;
vis[xu] = true;
for (int i = h[xu]; i; i = e[i].ne) {
int u = e[i].t;
if (dis[u] > dis[xu] + e[i].w) {
dis[u] = dis[xu] + e[i].w;
q.push({dis[u], u});
}
}
}
}
int main() {
freopen("jump.in", "r", stdin);
freopen("jump.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> s;
cin >> k;
int x, y;
long long w;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
cin >> w;
add(x, y, w);
add(y, x, w);
}
if (n <= 800) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) continue;
if (s == 1) add(i, j, k * 1ll * (i & j));
if (s == 2) add(i, j, k * 1ll * (i ^ j));
if (s == 3) add(i, j, k * 1ll * (i | j));
}
}
dij(1, true);
for (int i = 2; i <= n; i++) {
cout << dis[i] << ' ';
}
return 0;
}
p[0] = 1;
vi[0] = true;
for (int j = 1; j <= 18; j++) {
p[j] = p[j - 1] * 2;
vi[p[j]] = true;
}
if (s == 1) {
for (int j = 18; j >= 0; j--) {
if (p[j] == n) {
for (int i = 2; i <= n; i++) {
cout << 0 << ' ';
}
return 0;
}
}
for (int j = 1; j <= 18; j++) {
if (p[j] > n) break;
add(p[j - 1], p[j], 0);
add(p[j], p[j - 1], 0);
}
for (int i = 2; i <= n; i++) {
if (vi[i]) continue;
add(1, i, k * 1ll * (1 & i));
add(i, 1, k * 1ll * (1 & i));
}
for (int i = 2; i <= n; i++) {
if (vi[i]) continue;
for (int j = 0; j <= 18; j++) {
if (p[j] > n) break;
if (!((1 << j) & i)) {
add(p[j], i, 0);
add(i, p[j], 0);
}
}
}
dij(1, true);
for (int i = 2; i <= n; i++) {
cout << dis[i] << ' ';
}
return 0;
}
if (s == 2) {
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= 18; j++) {
if (p[j] > n) break;
add(i, (i ^ (1 << j)), k * p[j]);
add((i ^ (1 << j)), i, k * p[j]);
}
}
dij(1, true);
for (int i = 2; i <= n; i++) {
cout << dis[i] << ' ';
}
return 0;
}
if (s == 3) {
memset(f, 0x3f, sizeof(f));
f[1] = 0;
for (int i = 2; i <= n; i++) {
add(1, i, k * (1 | i));
add(i, 1, k * (1 | i));
}
dij(1, true);
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= 18; j++) {
if (i & (1 << j)) {
dis[i] = min(dis[i], f[i ^ (1 << j)] + k * i);
f[i] = min(dis[i], f[i ^ (1 << j)]);
}
}
}
dij(1, false);
for (int i = 2; i <= n; i++) cout << dis[i] << ' ';
}
return 0;
}
区间 (interval) 15pts
暴力扫
对于
怎样预处理?对于每一个数维护它的右边第一个大于等于它的数,那么右端点的范围就确定了,直接预处理
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, q;
int a[500005], b[500005], c[1000005], cnt;
int pos[500005];
int ans[5005][5005];
int main() {
freopen("interval.in", "r", stdin);
freopen("interval.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
c[++cnt] = a[i];
}
for (int i = 2; i <= n; i++) {
cin >> b[i];
c[++cnt] = b[i];
}
sort(c + 1, c + 1 + cnt);
cnt = unique(c + 1, c + 1 + cnt) - c - 1;
for (int i = 1; i <= n; i++) {
pos[i] = n;
for (int j = i + 1; j <= n; j++) {
if (a[j] >= a[i]) {
pos[i] = j;
break;
}
}
}
cin >> q;
int l = 0, r = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= pos[i]; j++) {
ans[i][j] = ans[i][j - 1];
if (b[j] > a[i]) ans[i][j]++;
}
}
for (int i = 1; i <= q; i++) {
cin >> l >> r;
int sum = 0;
for (int j = l; j <= r - 1; j++) {
sum += ans[j][min(pos[j], r)];
}
cout << sum << '\n';
}
return 0;
}
岛屿 5pts
居然都不给暴搜的部分分。。。;
正解,考虑 然后懒了,直接粘题解把。。。;
就是考虑每次加一对点,然后将其与已经加过的点连边,注意计算概率;
最后的求和其实就是递归一层层往下套得来的;
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <iomanip>
using namespace std;
int x, y, n;
double ans;
int main() {
freopen("island.in", "r", stdin);
freopen("island.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> x >> y;
for (int i = 1; i <= y; i++) ans += 1.00 / (2 * x + i);
for (int i = 1; i <= x; i++) ans += 1.00 / (2 * i - 1);
cout << fixed << setprecision(15) << ans;
return 0;
}
最短路 26pts
重测了好多次,然后就整了个26pts;
首先建出最短路树,然后考虑删一条点
发现并不怎么好搞,所以我们枚举每一条非树边,看看它能有什么贡献;
那么对于非树边
简而言之,它们可以对树上
对于一个点
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
int n, m;
struct sss{
int t, ne;
}e[500005];
int h[500005], cnt;
void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
vector<pair<int, long long> > v[500005];
long long dis[500005];
bool vis[500005];
int pre[500005];
void dij(int x) {
for (int i = 1; i <= n; i++) {
dis[i] = 0x3f3f3f3f3f3f3f3f;
vis[i] = false;
}
priority_queue<pair<long long, int>, vector<pair<long long, int> >, greater<pair<long long, int> > > q;
while(!q.empty()) q.pop();
dis[x] = 0;
q.push({0, x});
while(!q.empty()) {
int xu = q.top().second;
q.pop();
if (vis[xu]) continue;
vis[xu] = true;
for (int i = 0; i < v[xu].size(); i++) {
int u = v[xu][i].first;
if (dis[u] > dis[xu] + v[xu][i].second) {
dis[u] = dis[xu] + v[xu][i].second;
pre[u] = xu;
q.push({dis[u], u});
}
}
}
}
int fa[500005], siz[500005], dep[500005], hson[500005], htop[500005], dfn[500005], dcnt;
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r;
long long mi, lz;
}tr[500005];
inline void push_down(int id) {
if (tr[id].lz != 0x3f3f3f3f3f3f3f3f) {
tr[ls(id)].lz = min(tr[ls(id)].lz, tr[id].lz);
tr[rs(id)].lz = min(tr[rs(id)].lz, tr[id].lz);
tr[ls(id)].mi = min(tr[ls(id)].mi, tr[id].lz);
tr[rs(id)].mi = min(tr[rs(id)].mi, tr[id].lz);
tr[id].lz = 0x3f3f3f3f3f3f3f3f;
}
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
tr[id].mi = tr[id].lz = 0x3f3f3f3f3f3f3f3f;
if (l == r) return;
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
}
void add(int id, int l, int r, long long d) {
if (tr[id].l >= l && tr[id].r <= r) {
tr[id].lz = min(tr[id].lz, d);
tr[id].mi = min(tr[id].mi, d);
return;
}
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (l <= mid) add(ls(id), l, r, d);
if (r > mid) add(rs(id), l, r, d);
}
long long ask(int id, int pos) {
if (tr[id].l == tr[id].r) return tr[id].mi;
push_down(id);
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) return ask(ls(id), pos);
else return ask(rs(id), pos);
}
}
namespace TCS{
void dfs1(int x, int f) {
fa[x] = f;
hson[x] = -1;
siz[x] = 1;
dep[x] = dep[f] + 1;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == f) continue;
dfs1(u, x);
siz[x] += siz[u];
if (hson[x] == -1 || siz[hson[x]] < siz[u]) hson[x] = u;
}
}
void dfs2(int x, int t) {
htop[x] = t;
dfn[x] = ++dcnt;
if (hson[x] == -1) return;
dfs2(hson[x], t);
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == fa[x] || u == hson[x]) continue;
dfs2(u, u);
}
}
void add(int x, int y, long long d) {
int u = x, v = y;
while(htop[x] != htop[y]) {
if (dep[htop[x]] < dep[htop[y]]) swap(x, y);
SEG::add(1, dfn[htop[x]], dfn[x], dis[u] + dis[v] + d);
x = fa[htop[x]];
}
if (x == y) return;
if (dfn[x] > dfn[y]) swap(x, y);
SEG::add(1, dfn[x] + 1, dfn[y], dis[u] + dis[v] + d);
}
}
int main() {
freopen("path.in", "r", stdin);
freopen("path.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
long long w;
cnt = 1;
for (int i = 1; i <= m; i++) {
cin >> x >> y;
cin >> w;
v[x].push_back({y, w});
v[y].push_back({x, w});
}
dij(1);
for (int i = 2; i <= n; i++) {
add(i, pre[i]);
add(pre[i], i);
}
TCS::dfs1(1, 0);
TCS::dfs2(1, 1);
SEG::bt(1, 1, dcnt);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < v[i].size(); j++) {
if ((fa[i] == v[i][j].first || fa[v[i][j].first] == i) && v[i][j].second == (dis[max(i, v[i][j].first)] - dis[min(i, v[i][j].first)])) continue;
TCS::add(i, v[i][j].first, v[i][j].second);
}
}
for (int i = 2; i <= n; i++) {
long long val = SEG::ask(1, dfn[i]);
if (val != 0x3f3f3f3f3f3f3f3f) cout << val - dis[i] << '\n';
else cout << -1 << '\n';
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!