20220506模拟赛
溜冰
题意
\(n\) 点 \(m\) 边,高度为 \(h_i\) ,若经过边\((u,v)\) 从 \(u\) 到 \(v\)
- \(h_u<h_v\) ,开心值减少 \(2(h_v-h_u)\)
- \(h_u>h_v\) ,开心值增加 \(h_u-h_v\)
- \(h_u=h_v\) ,开心值不便
初始在点 1 ,开心值为 0,求溜冰时最大开心值
\(n,m\le 2\times 10^5,h_i\le 10^8\)
无重边、自环
sol & code
直接连边跑 dijkstra ,有负权边,考虑转换
考虑将从起点到点 \(i\) 有固定开心值 \(h_1-h_i\) ,此处 \(h_1>h_i\) ,否则不是最优
则要到点 \(i\) 减少的开心值最少
若 \(h_u>h_v\) ,且经过中转点 \(k\) ,\(h_u<h_k\) ,即从 \(u\rightarrow k\rightarrow v\)
则最终开心值为 \(-2(h_k-h_u)+(h_k-h_v)=h_u-h_k+(h_u-h_v)\)
相比原来开心值,减少了 \(h_k-h_u\)
所以若 \(h_u<h_k\) ,连 \((u,k,h_k-h_u),(k,u,0)\) 两条边,反之同理。
跑最短路即可
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 200005;
int n, m, a[N], lst[N], Ecnt, vis[N];
LL dis[N], ans;
struct Ed { int to, nxt; LL qz; } e[N << 1];
inline void Ae(int fr, int go, int vl) {
e[++Ecnt] = (Ed){ go, lst[fr], 1ll * vl }, lst[fr] = Ecnt;
}
struct P {
int x; LL d;
bool operator < (P A) const {
return d > A.d;
}
} ;
priority_queue<P> Q;
inline void dij() {
for (int i = 1; i <= n; i++) dis[i] = 1e15;
dis[1] = 0, Q.push((P){ 1, 0 });
while (!Q.empty()) {
register int u = Q.top().x; Q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (int i = lst[u], v; i; i = e[i].nxt)
if (dis[u] + e[i].qz < dis[v = e[i].to])
dis[v] = dis[u] + e[i].qz, Q.push((P){ v, dis[v] });
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1, u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
if (a[u] <= a[v]) Ae(u, v, a[v] - a[u]), Ae(v, u, 0);
if (a[u] > a[v]) Ae(u, v, 0), Ae(v, u, a[u] - a[v]);
}
dij();
for (int i = 2; i <= n; i++)
if (a[i] < a[1] && dis[i] < 1e15) ans = max(ans, a[1] - a[i] - dis[i]);
printf("%lld", ans);
}
满足条件的序列个数
题意 & sol
求满足以下条件的序列的个数,答案 \(\;mod\;998244353\)
- 序列的长度为 \(n\)
- 元素在 \([1,m]\) 之间
- 最长上升子序列长度刚好为 3
\(3\le n\le 1000, 3\le m\le 10\)
设 \(f_{i,k1,k2,k3}\) 表示前 \(i\) 个数,长度为 1 和 2 和 3 的最长上升子序列结尾的最小值为 \(k1,k2,k3\) 的方案
枚举 \(i\) 位选 \(j\)
- \(j\le k1\) ,\(f_{i,k1,k2,k3}\rightarrow f_{i+1,j,k2,k3}\)
- \(k1<j\le k2\) ,\(f_{i,k1,k2,k3}\rightarrow f_{i+1,k1,j,k3}\)
- \(k2<j\le k3\) ,\(f_{i,k1,k2,k3}\rightarrow f_{i+1,k1,k2,j}\)
\(f_{0,m+1,m+1,m+1}=1\)
code
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const LL P = 998244353;
int n, m;
LL f[1005][15][15][15], ans;
int main() {
scanf("%d%d", &n, &m);
f[0][m + 1][m + 1][m + 1] = 1;
for (int i = 0; i < n; i++)
for (int k1 = 1; k1 <= m + 1; k1++)
for (int k2 = k1; k2 <= m + 1; k2++)
for (int k3 = k2; k3 <= m + 1; k3++)
for (int j = 1; j <= m; j++) {
if (j <= k1) (f[i + 1][j][k2][k3] += f[i][k1][k2][k3]) %= P;
else if (j <= k2) (f[i + 1][k1][j][k3] += f[i][k1][k2][k3]) %= P;
else if (j <= k3) (f[i + 1][k1][k2][j] += f[i][k1][k2][k3]) %= P;
}
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= m; k++)
(ans += f[n][i][j][k]) %= P;
printf("%lld", ans);
}
区间排序
题意 & sol
1 到 \(n\) 的排列 \(P\) 和整数 \(X\) ,\(Q\) 次操作对区间升序或降序排序。
求最后 X 的位置。
\(n\le 2*10^5\)
\(X\) 的位置只和排名有关,将 \(P\) 分为 \(\le X\) 和 \(>X\) 两类
可以先查询排名 \(rk\)
则升序排序可以转为 \([l,l+rk-1]\) 和 \([l+rk,r]\) 的区间修改操作
用线段树区间查询、修改,维护排名
code
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
typedef long double LD;
typedef long long LL;
typedef double db;
const int N = 200005, SQ = 450;
int n, Ti, X, a[N], pos, tg[N << 2], sm[N << 2], res;
#define ls (rt << 1)
#define rs (rt << 1 | 1)
inline void Up(int rt) {
sm[rt] = sm[ls] + sm[rs];
}
void bui(int l, int r, int rt) {
tg[rt] = -1;
if (l == r) { sm[rt] = a[l]; return; }
register int mid = l + r >> 1;
bui(l, mid, ls), bui(mid + 1, r, rs), Up(rt);
}
inline void down(int l, int r, int rt) {
if (tg[rt] == -1) return;
register int mid = l + r >> 1;
tg[ls] = tg[rt], sm[ls] = (mid - l + 1) * tg[rt];
tg[rs] = tg[rt], sm[rs] = (r - mid) * tg[rt];
tg[rt] = -1;
}
void ask(int ql, int qr, int l, int r, int rt) {
if (ql > r || l > qr) return;
if (ql <= l && r <= qr) { res += sm[rt]; return; }
register int mid = l + r >> 1; down(l, r, rt);
ask(ql, qr, l, mid, ls), ask(ql, qr, mid + 1, r, rs);
}
void mdy(int ql, int qr, int vl, int l, int r, int rt) {
if (ql > r || l > qr) return;
if (ql <= l && r <= qr) { tg[rt] = vl, sm[rt] = (r - l + 1) * vl; return; }
register int mid = l + r >> 1; down(l, r, rt);
mdy(ql, qr, vl, l, mid, ls);
mdy(ql, qr, vl, mid + 1, r, rs), Up(rt);
}
#undef ls
#undef rs
int main() {
scanf("%d%d%d", &n, &Ti, &X);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if (a[i] == X) pos = i;
a[i] = (a[i] <= X);
}
bui(1, n, 1);
for (int op, x, y; Ti--; ) {
scanf("%d%d%d", &op, &x, &y);
res = 0, ask(x, y, 1, n, 1);
if (op == 1) {
if (res) mdy(x, x + res - 1, 1, 1, n, 1);
mdy(x + res, y, 0, 1, n, 1);
if (x <= pos && pos <= y) pos = x + res - 1;
} else {
if (res) mdy(y - res + 1, y, 1, 1, n, 1);
mdy(x, y - res, 0, 1, n, 1);
if (x <= pos && pos <= y) pos = y - res + 1;
}
}
printf("%d", pos);
}
总结
- 最短路的转化,
dijkstra 不能跑负权(小声) - LIS 的状态表示
- 区间排序转化成排名