[赛记] NOIP2024加赛7
镜的绮想 (mirror) 100pts
考虑
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, m;
bool vis[3000005];
vector<int> shi[3000005], xu[3000005], v;
int sum[5000005];
int main() {
freopen("mirror.in", "r", stdin);
freopen("mirror.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int x, y;
for (int i = 1; i <= n; i++) {
cin >> x >> y;
x += 1e6;
y += 1e6;
shi[x].push_back(y);
if (!vis[x]) {
vis[x] = true;
v.push_back(x);
}
}
for (int i = 1; i <= m; i++) {
cin >> x >> y;
x += 1e6;
y += 1e6;
xu[x].push_back(y);
if (!vis[x]) {
vis[x] = true;
v.push_back(x);
}
}
int ans = 0;
for (int i = 0; i < v.size(); i++) {
for (int j = 0; j < shi[v[i]].size(); j++) {
for (int k = 0; k < xu[v[i]].size(); k++) {
sum[(shi[v[i]][j] + xu[v[i]][k])]++;
ans = max(ans, sum[(shi[v[i]][j] + xu[v[i]][k])]);
}
}
}
cout << ans;
return 0;
}
万物有灵 (animism) -pts
赛时不会独立集,所以没写;
考虑独立集就是一个图中的点集,其中任意两点没有边相连;
那么对于这棵树,我们发现,它的最大独立集是从最后一层开始,每隔一层选一层,最后选到根,因为每往下走,它的节点数只增不减;
这样朴素实现是
发现它有一个循环节,每
这样就可以矩阵加速,所以时间复杂度:
细节有些多;
点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
long long nn, k, mod;
long long a[2000005];
struct Mat{
long long a[5][5];
int n, m;
inline void clear() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = 0;
}
}
}
inline void one() {
for (int i = 1; i <= n; i++) a[i][i] = 1;
}
inline void rsize(int x, int y) {
n = x;
m = y;
}
inline Mat operator *(const Mat &A) const {
Mat ans;
ans.rsize(n, A.m);
ans.clear();
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= A.m; j++) {
for (int k = 1; k <= m; k++) {
ans.a[i][j] = (ans.a[i][j] + a[i][k] * A.a[k][j] % mod) % mod;
}
}
}
return ans;
}
};
Mat ksm(Mat A, long long b) {
Mat ans;
ans.rsize(2, 2);
ans.clear();
ans.one();
while(b) {
if (b & 1) ans = ans * A;
A = A * A;
b >>= 1;
}
return ans;
}
long long qpow(long long a, long long b) {
long long ans = 1;
while(b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int main() {
freopen("animism.in", "r", stdin);
freopen("animism.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> nn >> k >> mod;
for (int i = 1; i <= k; i++) {
cin >> a[i];
a[i + k] = a[i];
}
k *= 2;
if (nn <= k) {
long long ans = 1;
long long now = 1;
if (!(nn & 1)) {
ans = 1;
for (int i = 1; i <= nn; i++) {
now = now * a[i] % mod;
if (!(i & 1)) ans = (ans + now) % mod;
}
} else {
ans = 0;
for (int i = 1; i <= nn; i++) {
now = now * a[i] % mod;
if (i & 1) ans = (ans + now) % mod;
}
}
cout << ans;
return 0;
}
long long w = 1, f = 0;
for (int i = 1; i <= k; i++) {
w = w * a[i] % mod;
if (!(nn & 1)) {
if (!(i & 1)) f = (f + w) % mod;
} else {
if (i & 1) f = (f + w) % mod;
}
}
Mat B;
B.rsize(2, 2);
B.a[1][1] = w; B.a[1][2] = 1; B.a[2][1] = 0; B.a[2][2] = 1;
long long res = (nn + (nn & 1)) / k;
B = ksm(B, res - 1);
long long now = qpow(w, res);
long long ans = (B.a[1][1] * f % mod + B.a[1][2] * f % mod) % mod;
if (!(nn & 1)) ans = (ans + 1) % mod;
long long ret = nn % k;
for (int i = 1; i <= ret; i++) {
now = now * a[i] % mod;
if (ret & 1) {
if (i & 1) ans = (ans + now) % mod;
} else {
if (!(i & 1)) ans = (ans + now) % mod;
}
}
cout << ans;
return 0;
}
白石溪 (creek) 30pts
暴力DP还挂了15pts。。。
DP不好优化,考虑贪心;
我们钦定一开始全是蓝色,那么每次把一个蓝色变成红色,贡献为 a[i] - b[i] + (i - 1) * d + (n - i) * c - now * (d + c)
,其中 a[i] - b[i] + (i - 1) * d + (n - i) * c
排序选最大即可;
时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
long long n, c, d;
long long f[2][1000005];
long long a[1000005], b[1000005];
priority_queue<long long> q;
int main() {
freopen("creek.in", "r", stdin);
freopen("creek.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> c >> d;
long long ans = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
ans += b[i];
q.push(a[i] - b[i] + (i - 1) * d + (n - i) * c);
}
long long now = 0;
long long sum = ans;
while(!q.empty()) {
long long t = q.top();
q.pop();
sum += t - now * (d + c);
ans = max(ans, sum);
now++;
}
cout << ans;
return 0;
}
上山岗 (uphill) 15pts
我们首先让人从小到大选山,如果他能选就选最后面的,可以发现,这样是可以得到一个最大解的,这个直接线段树二分即可解决;
考虑将大数前移,不难发现,如果这个数在前一步操作中匹配上了,那么我们只能将其移到没有匹配的山去匹配(不会出现和小数互换的情况,因为如果能换,这个位置就是较小的那个数了)。如果没有匹配,考虑他能不能将一个小数换掉,或者去一个空位上(前面大的数留下来的),两者取最大即可;
开两棵线段树,然后线段树二分即可解决这个问题,时间复杂度:
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n;
int a[500005], b[500005], pos[500005], ans[500005];
bool vis[500005];
vector<int> v;
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, mi;
}tr[3000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
tr[id].mi = a[l];
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
push_up(id);
}
void bt1(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
if (!vis[l]) tr[id].mi = a[l];
else tr[id].mi = 2e9;
return;
}
int mid = (l + r) >> 1;
bt1(ls(id), l, mid);
bt1(rs(id), mid + 1, r);
push_up(id);
}
int ask(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[rs(id)].mi < d) return ask(rs(id), d);
else return ask(ls(id), d);
}
int askk(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[ls(id)].mi < d) return askk(ls(id), d);
else return askk(rs(id), d);
}
void del(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = 2e9;
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) del(ls(id), pos);
else del(rs(id), pos);
push_up(id);
}
void add(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = a[tr[id].l];
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) add(ls(id), pos);
else add(rs(id), pos);
push_up(id);
}
}
namespace seg{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, mi;
}tr[3000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
if (l == r) {
if (vis[l]) tr[id].mi = a[l];
else tr[id].mi = 2e9;
return;
}
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
push_up(id);
}
void del(int id, int pos) {
if (tr[id].l == tr[id].r) {
tr[id].mi = 2e9;
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) del(ls(id), pos);
else del(rs(id), pos);
push_up(id);
}
int ask(int id, int d) {
if (tr[id].l == tr[id].r) return tr[id].l;
if (tr[ls(id)].mi < d) return ask(ls(id), d);
else return ask(rs(id), d);
}
int askk(int id, int pos) {
if (tr[id].l == tr[id].r) return tr[id].mi;
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) return askk(ls(id), pos);
else return askk(rs(id), pos);
push_up(id);
}
}
int main() {
freopen("uphill.in", "r", stdin);
freopen("uphill.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];
}
SEG::bt(1, 1, n);
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++) {
if (SEG::tr[1].mi >= b[i]) continue;
pos[i] = SEG::ask(1, b[i]);
vis[pos[i]] = true;
SEG::del(1, pos[i]);
}
seg::bt(1, 1, n);
SEG::bt1(1, 1, n);
for (int i = n; i >= 1; i--) {
int p = seg::askk(1, pos[i]);
if (p == 2e9) {
if (seg::tr[1].mi >= b[i]) {
v.push_back(b[i]);
continue;
}
int now = seg::ask(1, b[i]);
int ma = SEG::askk(1, 1e9);
if (now < ma) {
ans[now] = b[i];
seg::del(1, now);
} else {
ans[ma] = b[i];
SEG::del(1, ma);
}
} else {
SEG::add(1, pos[i]);
seg::del(1, pos[i]);
int now = SEG::askk(1, b[i]);
ans[now] = b[i];
SEG::del(1, now);
}
}
int now = 0;
for (int i = 1; i <= n; i++) {
if (!ans[i]) {
cout << v[now] << ' ';
now++;
} else cout << ans[i] << ' ';
}
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框架的用法!