2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛
A
观察到 next_permutation
即可。
Code
#include <bits/stdc++.h>
typedef long long LL;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, k;
std::cin >> n >> k;
std::vector<int> p(n);
std::iota(p.begin(), p.end(), 0);
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
int ans = 0;
do {
bool ok = true;
for (int i = 0; i < n; i++) {
if (i && std::abs(a[p[i]] - a[p[i - 1]]) < k) {
ok = false;
break;
}
}
if (ok) {
ans++;
}
} while (std::next_permutation(p.begin(), p.end()));
std::cout << ans << '\n';
return 0;
}
B
首先观察到这棵树是一个满三叉树,对于删除一个子树
然后注意到,如果以
同时注意,如果一个点
复杂度是
Code
#include <bits/stdc++.h>
typedef long long LL;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::unordered_map<LL, LL> mp;
std::unordered_set<LL> se;
auto calc = [&](LL k) {
LL cnt = 0, v = 1;
int dep = 0;
while (cnt < k) {
cnt += v;
v *= 3;
dep++;
}
LL sum = 0;
v = 1;
for (int i = 1; i <= n - dep + 1; i++) {
sum += v;
v *= 3;
}
return sum;
};
LL tot = 0, v = 1;
for (int i = 1; i <= n; i++) {
tot += v;
v *= 3;
}
while (m--) {
LL k;
std::cin >> k;
LL nk = k;
bool ok = false;
while (k) {
if (se.count(k)) {
ok = true;
break;
}
if (k % 3 == 0) k /= 3;
else if (k % 3 == 1) k = (k - 1) / 3;
else k = (k + 1) / 3;
}
if (ok) {
std::cout << tot << '\n';
continue;
}
k = nk;
se.insert(k);
LL cnt = calc(k) - mp[k];
tot -= cnt;
std::cout << tot << '\n';
while (k) {
mp[k] += cnt;
if (k % 3 == 0) k /= 3;
else if (k % 3 == 1) k = (k - 1) / 3;
else k = (k + 1) / 3;
}
}
return 0;
}
C
观察到对于一个完全平方数
这是非常经典的二分图问题,我们考虑
由于值域
时间复杂度为
判断二分图我们可以用 dfs 染色法,或者扩展域并查集。
Code
#include <bits/stdc++.h>
typedef long long LL;
const int N = 2e5 + 10, M = 2e5;
bool st[N];
int a[N], p[2 * N];
int find(int x) {
return x == p[x] ? x : p[x] = find(p[x]);
}
void solve() {
int n;
std::cin >> n;
for (int i = 0; i < n; i++) {
std::cin >> a[i];
st[a[i]] = true;
}
for (int i = 1; i <= 2 * M; i++) {
p[i] = i;
}
for (int i = 0; i < n; i++) {
for (int j = (int)sqrt(a[i]) + 1; j * j - a[i] <= M; j++) {
if (st[j * j - a[i]] && j * j - a[i] != a[i]) {
p[find(a[i])] = find(j * j - a[i] + M);
p[find(j * j - a[i])] = find(a[i] + M);
}
}
}
for (int i = 0; i < n; i++) {
st[a[i]] = false;
}
for (int i = 0; i < n; i++) {
if (find(a[i]) == find(a[i] + M)) {
std::cout << "NO\n";
return;
}
}
std::cout << "YES\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
D
观察到如下性质:
- 合并的顺序,不影响最终获得的能量。
所以如果用了操作之后的序列为:
那么其最终获得的能量一定是固定的:
现在我们考虑区间
不难想到前缀和,我们可以考虑定义如下:
。
那么我们枚举右端点
不难发现只要维护
Code
#include <bits/stdc++.h>
typedef long long LL;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, k;
std::cin >> n >> k;
std::vector<LL> d(n + 1, 0), a(n + 1), b(n + 1);
LL ans = -1e18;
for (int i = 1; i <= n; i++) {
std::cin >> a[i] >> b[i];
if (i > 1) {
d[i - 1] = a[i - 1] * b[i];
}
}
std::vector<LL> suf(n + 1, 0);
for (int i = n - 1; i >= 1; i--) {
suf[i] = suf[i + 1] + d[i];
}
LL pre = 0, maxv = 0;
for (int i = 1; i <= n; i++) {
ans = std::max(ans, pre * k * k + maxv + suf[i] + d[i] * (k - 1));
pre += d[i];
maxv = std::max(maxv, pre + d[i] * (k - 1) - k * k * pre);
}
std::cout << ans << '\n';
return 0;
}
E
比较裸的 djstla,在松弛的时候判断绿,黄,红灯的变换,来判断是否要停下,注意到终点了就不用在通过交通灯了。
Code
#include <bits/stdc++.h>
typedef long long LL;
typedef std::pair<int, int> PII;
const int N = 1e4 + 10, INF = 1e9;
int d[N];
bool st[N];
std::vector<std::pair<int, int>> g[N];
int a[N][3];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, s, e;
while (std::cin >> n >> m >> s >> e, n || m || s || e) {
for (int i = 0; i < n; i++) {
g[i].clear();
for (int j = 0; j < 3; j++) {
std::cin >> a[i][j];
}
}
while (m--) {
int u, v, w;
std::cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
for (int i = 0; i < n; i++) {
d[i] = INF;
st[i] = false;
}
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> heap;
d[s] = 5;
heap.push({d[s], s});
while (heap.size()) {
int u = heap.top().second;
heap.pop();
if (st[u]) continue;
st[u] = true;
for (auto [v, w] : g[u]) {
int t = (d[u] + w) % (a[v][0] + a[v][1] + a[v][2]);
int tis = 0;
if (t >= a[v][0] + a[v][1]) {
tis += a[v][0] + a[v][1] + a[v][2] - t + 5;
}
if (v == e) tis = 0;
if (d[v] > d[u] + w + tis) {
d[v] = d[u] + w + tis;
heap.push({d[v], v});
}
}
}
std::cout << d[e] / 60 << ":";
if (d[e] % 60 < 10) std::cout << 0;
std::cout << d[e] % 60 << "\n";
}
return 0;
}
F
观察到,最后满足
可以证明只要选出的子序列是严格递增的,我们一定可以通过操作使这些位置都满足条件。
我们可以先把除了选出来的数的其他数删了。
例如我们删完后为:
那么我们显然可以通过插入
故问题转化为求最长上升子序列。
Code
#include <bits/stdc++.h>
typedef long long LL;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> p;
for (int i = 1; i <= n; i++) {
int x;
std::cin >> x;
if (p.size() == 0 || x > p.back()) p.push_back(x);
else *std::lower_bound(p.begin(), p.end(), x) = x;
}
std::cout << p.size() << '\n';
return 0;
}
G
很裸的 DP,令
枚举边集转移即可,复杂度
Code
#include <bits/stdc++.h>
typedef long long LL;
const LL INF = 1e18, N = 5010;
LL dp[N], ndp[N];
int a[N];
std::vector<int> g[N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, k;
std::cin >> n >> m >> k;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
while (m--) {
int u, v;
std::cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
dp[i] = -INF;
}
dp[1] = a[1];
for (int i = 2; i <= k; i++) {
for (int j = 1; j <= n; j++) {
ndp[j] = -INF;
}
for (int j = 1; j <= n; j++) {
for (auto c : g[j]) {
if (c != j) ndp[c] = std::max(ndp[c], dp[j] + a[c]);
}
}
for (int j = 1; j <= n; j++) {
dp[j] = ndp[j];
}
}
if (dp[1] <= 0) {
std::cout << -1 << '\n';
} else {
std::cout << dp[1] << '\n';
}
return 0;
}
H
枚举
直接维护一个值域线段树(离散化,或者动态开点即可),然后在线段树上面二分即可。
Code
#include <bits/stdc++.h>
typedef long long LL;
const int N = 1e5 + 10, INF = 1e9;
int a[N], root[N], idx;
struct node {
int l, r;
int cnt;
LL sum;
} tr[40 * N];
void insert(int &u, int v, int l, int r, int x) {
u = ++idx;
tr[u] = tr[v];
tr[u].cnt++, tr[u].sum += x;
if (l == r) return;
int mid = (l + r) >> 1;
if (x <= mid) insert(tr[u].l, tr[v].l, l, mid, x);
else insert(tr[u].r, tr[v].r, mid + 1, r, x);
}
int query(int u, int l, int r, int k) {
if (l == r) {
if (l == 0) return tr[u].cnt;
return std::min(k / l, tr[u].cnt);
}
int mid = (l + r) >> 1;
if (tr[tr[u].l].sum <= k) {
return tr[tr[u].l].cnt + query(tr[u].r, mid + 1, r, k - tr[tr[u].l].sum);
}
return query(tr[u].l, l, mid, k);
}
void solve() {
int n, m;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
root[i] = 0;
}
idx = 0;
for (int i = 1; i <= n; i++) {
int cost = m - a[i];
std::cout << i - 1 - query(root[i - 1], 0, INF, cost) << " \n"[i == n];
insert(root[i], root[i - 1], 0, INF, a[i]);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
I
式子可以转化为:
提前算好
Code
#include <bits/stdc++.h>
typedef long long LL;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, q;
std::cin >> n >> q;
//a ^ 2 - 2ax + x ^ 2
LL s1 = 0, s2 = 0;
for (int i = 1; i <= n; i++) {
int x;
std::cin >> x;
s1 += 1LL * x * x;
s2 += 2 * x;
}
while (q--) {
int x;
std::cin >> x;
std::cout << s1 - x * s2 + 1LL * n * x * x << '\n';
}
return 0;
}
J
看到
我们把
那么我们的坐标变换就变成直接模
其中
时间复杂度为
最后要映射回去,即若
Code
#include <bits/stdc++.h>
typedef long long LL;
LL n, sx, sy, dx, dy, t;
LL f[6][6], g[6][6] = {
{2, 1, 1, 1, 0, 0},
{1, 2, 1, 1, 0, 0},
{1, 0, 1, 0, 0, 0},
{0, 1, 0, 1, 0, 0},
{1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 1}
};
void mul(LL a[][6], LL b[][6], LL c[][6]) {
static LL tmp[6][6];
std::memset(tmp, 0, sizeof tmp);
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j] % n) % n;
}
}
}
std::memcpy(c, tmp, sizeof tmp);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> sx >> sy >> dx >> dy >> t;
f[0][0] = sx % n, f[0][1] = sy % n, f[0][2] = dx % n, f[0][3] = dy % n, f[0][4] = 0, f[0][5] = 1;
while (t) {
if (t & 1) mul(f, g, f);
mul(g, g, g);
t >>= 1;
}
std::cout << (f[0][0] == 0 ? n : f[0][0]) << " " << (f[0][1] == 0 ? n : f[0][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框架的用法!