CSP-S 模拟赛34
CSP-S 模拟赛34
T1
考虑对原序列将
然而我们发现这样跑点的过程从前往后和从后往前是等价的。这样考虑的原因是发现这样的选数问题不具有方向性。于是时间复杂度
代码:
#include <bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int T;
int n, k;
int a[N];
int a1[N], a2[N];
int ct1, ct2;
int sm1[N], sm2[N];
int nx1[N], nx2[N];
void sve() {
memset(sm1, 0, sizeof sm1);
memset(sm2, 0, sizeof sm2);
memset(nx1, 0, sizeof nx1);
memset(nx2, 0, sizeof nx2);
cin >> n >> k;
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
ct1 = ct2 = 1;
a1[1] = a2[1] = 0;
for (int i = k; i > 1; i--)
a1[++ct1] = a[i];
for (int i = k + 1; i <= n; i++)
a2[++ct2] = a[i];
for (int i = 1; i <= ct1; i++)
sm1[i] = sm1[i - 1] + a1[i];
for (int i = 1; i <= ct2; i++)
sm2[i] = sm2[i - 1] + a2[i];
int mn = sm1[1], nw = 1;
for (int i = 2; i <= ct1; i++)
if (sm1[i] <= mn) {
nx1[nw] = i;
nw = i;
mn = sm1[i];
}
mn = sm1[ct1], nw = ct1;
for (int i = ct1 - 1; i >= 1; i--)
if (sm1[i] < mn) {
nx1[nw] = i;
nw = i;
mn = sm1[i];
}
mn = sm2[1], nw = 1;
for (int i = 2; i <= ct2; i++)
if (sm2[i] <= mn) {
nx2[nw] = i;
nw = i;
mn = sm2[i];
}
mn = sm2[ct2], nw = ct2;
for (int i = ct2 - 1; i >= 1; i--)
if (sm2[i] < mn) {
nx2[nw] = i;
nw = i;
mn = sm2[i];
}
if (sm1[ct1] + sm2[ct2] > 0)
return puts("No"), void();
int p1 = 1, p2 = 1;
while (nx1[p1] || nx2[p2]) {
if (!nx1[p1]) {
for (int j = p2 + 1; j <= nx2[p2]; j++)
if (sm1[p1] + sm2[j] > 0) {
puts("No");
return;
}
p2 = nx2[p2];
continue;
}
if (!nx2[p2]) {
for (int j = p1 + 1; j <= nx1[p1]; j++)
if (sm1[j] + sm2[p2] > 0) {
puts("No");
return;
}
p1 = nx2[p1];
continue;
}
int fg = 0;
for (int i = p1 + 1; i <= nx1[p1]; i++)
if (sm1[i] + sm2[p2] > 0) {
fg = 1;
break;
}
if (!fg) {
p1 = nx1[p1];
continue;
}
for (int j = p2 + 1; j <= nx2[p2]; j++)
if (sm1[p1] + sm2[j] > 0) {
puts("No");
return;
}
p2 = nx2[p2];
}
p1 = ct1, p2 = ct2;
while (nx1[p1] || nx2[p2]) {
if (!nx1[p1]) {
for (int j = p2 - 1; j >= nx2[p2]; j--)
if (sm1[p1] + sm2[j] > 0) {
puts("No");
return;
}
p2 = nx2[p2];
continue;
}
if (!nx2[p2]) {
for (int j = p1 - 1; j >= nx1[p1]; j--)
if (sm1[j] + sm2[p2] > 0) {
puts("No");
return;
}
p1 = nx2[p1];
continue;
}
int fg = 0;
for (int i = p1 - 1; i >= nx1[p1]; i--)
if (sm1[i] + sm2[p2] > 0) {
fg = 1;
break;
}
if (!fg) {
p1 = nx1[p1];
continue;
}
for (int j = p2 - 1; j >= nx2[p2]; j--)
if (sm1[p1] + sm2[j] > 0) {
puts("No");
return;
}
p2 = nx2[p2];
}
puts("Yes");
}
signed main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
cin >> T;
for (int i = 1; i <= T; i++)
sve();
return 0;
}
T2
显然考虑
朴素的 dp 定义是
考虑某一个位置
时间复杂度大抵是
这样考虑的原因是注意到每个最大值都会覆盖一个 "区间",因此可以由左右两端转移而来。
代码:
#include <bits/stdc++.h>
#define N 1005
#define int long long
using namespace std;
int n, k, p;
int C[N][N];
int dp[N][N][2];
void add(int &x, int y) {
x = (x + y + p) % p;
}
signed main() {
freopen("per.in", "r", stdin);
freopen("per.out", "w", stdout);
cin >> n >> k >> p;
C[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= i; j++)
C[i][j] = (C[i - 1][j] + (j > 0 ? C[i - 1][j - 1] : 0)) % p;
for (int i = 0; i <= k; i++)
dp[0][i][0] = dp[0][i][1] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= k; j++)
for (int t = 1; t <= i; t++) {
add(dp[i][j][0], C[i - 1][t - 1] * dp[t - 1][j - 1][1] % p * dp[i - t][j][0] % p);
add(dp[i][j][1], C[i - 1][t - 1] * ((dp[t - 1][j][1] * dp[i - t][j - 1][1] % p + dp[t - 1][j - 1][1] * dp[i - t][j][1] % p - dp[t - 1][j - 1][1] * dp[i - t][j - 1][1] % p) % p) % p);
}
int ans = 0;
for (int i = 1; i <= n; i++)
add(ans, C[n - 1][i - 1] * ((dp[i - 1][k][0] * dp[n - i][k][0] % p - dp[i - 1][k - 1][0] * dp[n - i][k - 1][0] % p)) % p % p);
cout << ans << "\n";
return 0;
}
T3
考虑问题的本质是从
对于正确性,考虑不用买门票的情形一定是去对回做贡献或是回对去做贡献,那这样的枚举是保证了某一方向先走到一个点再更新另一个方向,于是必然可以更新到正确答案。
代码:
#include <bits/stdc++.h>
#define N 255
using namespace std;
int n, m;
struct MP {
struct Node {
int to, nxt;
} e[N * N];
int head[N], cnt;
void add(int u, int v) {
e[++cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt;
}
} A, B;
int vl[N];
int dis[N][N];
bitset<N>can[N][N];
struct node {
int x, y;
int dis;
bool operator < (const node &x) const {
return dis > x.dis;
}
};
priority_queue<node>q;
int vis[N][N];
void Dij() {
dis[1][1] = vl[1];
can[1][1].set(1);
q.push((node) {
1, 1, dis[1][1]
});
while (!q.empty()) {
node tmp = q.top();
q.pop();
int x = tmp.x, y = tmp.y;
if (vis[x][y])
continue;
vis[x][y] = 1;
for (int i = A.head[x]; i; i = A.e[i].nxt) {
int p = A.e[i].to;
int ds = tmp.dis;
if (!can[x][y][p])
ds += vl[p];
if (ds < dis[p][y]) {
dis[p][y] = ds;
can[p][y] = can[x][y];
can[p][y].set(p);
q.push((node) {
p, y, dis[p][y]
});
}
}
for (int i = B.head[y]; i; i = B.e[i].nxt) {
int p = B.e[i].to;
int ds = tmp.dis;
if (!can[x][y][p])
ds += vl[p];
if (ds < dis[x][p]) {
dis[x][p] = ds;
can[x][p] = can[x][y];
can[x][p].set(p);
q.push((node) {
x, p, dis[x][p]
});
}
}
}
}
int main() {
freopen("tour.in", "r", stdin);
freopen("tour.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++)
scanf("%d", &vl[i]);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
A.add(x, y);
B.add(y, x);
}
memset(dis, 0x3f, sizeof dis);
Dij();
if (dis[n][n] < 0x3f3f3f3f)
cout << dis[n][n] << "\n";
else
puts("-1");
return 0;
}
T4
其实是简单题。矩形问题套路地考虑扫描线。将矩形按照
对于时间复杂度,更新上边的操作单次显然是
代码:
#include <bits/stdc++.h>
#define N 100005
#define M 100000
using namespace std;
int n;
int fa[N];
int fnd(int x) {
return x == fa[x] ? x : (fa[x] = fnd(fa[x]));
}
void mge(int x, int y) {
x = fnd(x), y = fnd(y);
if (x != y)
fa[x] = y;
}
struct Node {
int l, r;
int mx;
int fg;
int tp;
int id;
} e[N << 2];
#define lc (p << 1)
#define rc (lc | 1)
#define l(i) e[i].l
#define r(i) e[i].r
#define mx(i) e[i].mx
#define fg(i) e[i].fg
#define tp(i) e[i].tp
#define id(i) e[i].id
void push_up(int p) {
if (!tp(lc) || !tp(rc))
return tp(p) = 0, void();
if (id(lc) == id(rc)) {
tp(p) = 1;
mx(p) = mx(lc);
id(p) = id(lc);
}
else
tp(p) = 0;
}
void build(int p, int l, int r) {
l(p) = l, r(p) = r;
tp(p) = 1;
if (l == r)
return;
int mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
}
void push_down(int p) {
if (fg(p) == 0)
return;
fg(lc) = fg(rc) = 1;
mx(lc) = mx(rc) = mx(p);
id(lc) = id(rc) = id(p);
tp(lc) = tp(rc) = 1;
fg(p) = 0;
}
void update(int p, int l, int r, int mx, int id) {
if (l > r || l > r(p) || l(p) > r)
return;
if (l <= l(p) && r(p) <= r && tp(p)) {
if (mx(p) > mx)
return;
fg(p) = 1;
mx(p) = mx;
id(p) = id;
return;
}
push_down(p);
update(lc, l, r, mx, id);
update(rc, l, r, mx, id);
push_up(p);
}
void query(int p, int l, int r, int mx, int id) {
if (l > r || l > r(p) || l(p) > r)
return;
if (l <= l(p) && r(p) <= r && tp(p)) {
if (mx(p) >= mx)
mge(id(p), id);
return;
}
push_down(p);
query(lc, l, r, mx, id);
query(rc, l, r, mx, id);
}
struct node {
int x1, x2, y1, y2;
bool operator < (const node &x) const {
return y1 < x.y1;
}
} t[N];
int main() {
freopen("jux.in", "r", stdin);
freopen("jux.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d%d%d%d", &t[i].x1, &t[i].y1, &t[i].x2, &t[i].y2), fa[i] = i;
sort(t + 1, t + 1 + n);
build(1, 1, M);
for (int i = 1; i <= n; i++) {
query(1, t[i].x1, t[i].x2, t[i].y1, i);
update(1, t[i].x1, t[i].x2, t[i].y2, i);
}
unordered_map<int, int>mp;
for (int i = 1; i <= n; i++)
mp[fnd(i)] = 1;
cout << mp.size() << "\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】