20220506模拟赛

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 的状态表示
  • 区间排序转化成排名
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值