ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CLYZ-NOIP十连测 Day1

2021-10-03 12:31:28  阅读:135  来源: 互联网

标签:std NOIP int sum long Day1 del tot CLYZ


CLYZ-NOIP2021 国庆集训 B 组 Day1

题面:https://files.cnblogs.com/files/blogs/575626/day1B.zip

撰写博客

感觉很厉害,不明觉厉

我们令 \(dp_i\) 状态表示强制选择删除 \(i\) 位置并且干掉了 \(i\) 之前的和 \(i\) 所覆盖的线段的最小花费。

考虑状态转移,很显然就是

\[dp_i=\min_{pre_{i-1}}^{i-1}{dp_j}+w_i \]

其中 \(pre_{i}\) 为右端点为 \(i\) 的线段中,左端点最大值的前缀max。可以用 \(\text{KMP}\) 算法来预处理出来。

然后用单调队列转移即可。

时间复杂度为 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f, N = 2e5 + 10;
inline long long read()
{
    long long ret = 0;
    char ch = ' ', c = getchar();
    while (!(c >= '0' && c <= '9'))
        ch = c, c = getchar();
    while (c >= '0' && c <= '9')
        ret = (ret << 1) + (ret << 3) + c - '0', c = getchar();
    return ch == '-' ? -ret : ret;
}
int n, m, nxt[N], pre[N];
char s[N], c[11][N];
int a[N], dp[N], q[N], l = 1, r;

void pretreat(int op)
{
    int j = 0, len = strlen(c[op] + 1);
    for (int i = 1; i < len; i++) {
        while (j && c[op][j + 1] != c[op][i + 1])
            j = nxt[j];
        if (c[op][j + 1] == c[op][i + 1])
            j++;
        nxt[i + 1] = j;
    }
}
void kmp(int op)
{
    int j = 0, len = strlen(s + 1), lenc = strlen(c[op] + 1);
    for (int i = 0; i < len; i++) {
        pre[i + 1] = max(pre[i], pre[i + 1]);
        while (j && c[op][j + 1] != s[i + 1])
            j = nxt[j];
        if (c[op][j + 1] == s[i + 1])
            j++;
        if (j == lenc)
            pre[i + 1] = max(pre[i + 1], i + 1 - lenc + 1), j = nxt[j];
    }
}
int main()
{
	freopen("wzadx.in", "r", stdin);
	freopen("wzadx.out", "w", stdout);
    n = read(), m = read();
    scanf("%s", s + 1);
    for (int i = 1; i <= n; i++)
        a[i] = read();
    for (int i = 1; i <= m; i++) {
        scanf("%s", c[i] + 1);
        pretreat(i);
        kmp(i);
    }
    n++;
    l = 1, r = 0;
    for (int i = 0; i <= n; i++) {
        while (l <= r && q[l] < pre[i - 1])
            l++;
        dp[i] = dp[q[l]] + a[i];
        while (l <= r && dp[q[r]] >= dp[i])
            r--;
        q[++r] = i;
    }
    printf("%d", dp[n]);
    return 0;
}

逛动物园

我们考虑这玩意,每个笼子最开始的答案应该是 \(3^n\)。

然后发现每次合并,左边的应该会乘上 \(\frac{2}{3}\),右边的会乘上 \(\frac{1}{3}\)。

考虑离线下来,然后整一个类似于克鲁斯卡尔重构树的结构,用线段树维护 \(dfs\) 序列即可了。

似乎并没有什么方法可以在低于 \(O(n\log n)\) 的复杂度内解决。

#include <bits/stdc++.h>
using std::cerr;
using std::cin;
using std::cout;
using std::vector;
const int N = 4e5 + 10, mod = 998244353;
int n, m, f[N], op[N], x[N], y[N], dfc, st[N], ed[N], sz[N], F[N];
vector<int> v[N];
int ksm(int x, int y) {
    int res = 1;
    for (; y; y /= 2, x = (long long)x * x % mod) {
        if (y & 1) {
            res = (long long)res * x % mod;
        }
    }
    return res;
}
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
void dfs(int u) {
    st[u] = dfc + 1;
    if (u <= n && u) {
        ed[u] = ++dfc;
        return;
    }
    for (auto &j : v[u]) {
        dfs(j);
    }
    ed[u] = dfc;
}
inline int ls(int k) { return k << 1; }
inline int rs(int k) { return k << 1 | 1; }
int val[N * 4];
inline void down(int k) {
    if (val[k] != 1) {
        val[ls(k)] = (long long)val[ls(k)] * val[k] % mod;
        val[rs(k)] = (long long)val[rs(k)] * val[k] % mod;
        val[k] = 1;
    }
}
void modify(int k, int l, int r, int ql, int qr, int x) {
    if (ql <= l && r <= qr) {
        val[k] = (long long)val[k] * x % mod;
        return;
    }
    down(k);
    int mid = (l + r) >> 1;
    if (ql <= mid) {
        modify(ls(k), l, mid, ql, qr, x);
    }
    if (mid < qr) {
        modify(rs(k), mid + 1, r, ql, qr, x);
    }
    return;
}
int query(int k, int l, int r, int x) {
    if (l == r) {
        return val[k];
    }
    down(k);
    int mid = (l + r) >> 1;
    return x <= mid ? query(ls(k), l, mid, x) : query(rs(k), mid + 1, r, x);
}
void build(int k, int l, int r) {
    val[k] = l == r ? 3 : 1;
    if (l != r) {
        int mid = (l + r) >> 1;
        build(ls(k), l, mid);
        build(rs(k), mid + 1, r);
    }
}
int main() {
    freopen("zoo.in", "r", stdin);
    freopen("zoo.out", "w", stdout);
    std::ios::sync_with_stdio(0);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        f[i] = i;
    }
    int tot = n;
    for (int i = 1; i <= m; ++i) {
        cin >> op[i] >> x[i];
        if (op[i] == 1) {
            cin >> y[i];
            ++tot;
            f[tot] = tot;
            v[tot].push_back(find(x[i]));
            v[tot].push_back(find(y[i]));
            f[find(x[i])] = tot;
            f[find(y[i])] = tot;
        }
    }
    for (int i = 1; i <= tot; ++i) {
        if (i == f[i]) {
            v[0].push_back(i);
        }
    }
    dfs(0);
    memset(f, 0, sizeof f);
    for (int i = 1; i <= n; ++i) {
        f[i] = i;
        sz[i] = 1;
    }
    tot = n;
    build(1, 1, dfc);
    for (int i = 1; i <= m; ++i) {
        if (op[i] == 1) {
            int _x = find(x[i]), _y = find(y[i]);
            modify(1, 1, dfc, st[_x], ed[_x], 2 * ksm(3, sz[_y] - 1) % mod);
            modify(1, 1, dfc, st[_y], ed[_y], ksm(3, sz[_x] - 1));
            ++tot;
            f[_x] = f[_y] = f[tot] = tot;
            sz[tot] = sz[_x] + sz[_y];
        } else {
            cout << (long long)query(1, 1, dfc, st[x[i]]) * ksm(3, n - sz[find(x[i])]) % mod << '\n';
        }
    }
    return 0;
}

序列修改

感觉也很厉害。

首先手玩一下发现只可能修改每个值第一次出现的位置,不妨设我们修改的位置是 \(x\),那么肯定要修改成另外的一个值 \(a_y\), \(y\) 也是第一次出现。

考虑 \(y\) 和 \(x\) 的大小关系,如果在前边的话,收益就是 \(|a_x-a_y|-S_x+S_y\)。

否则的话,讨论一下应该有 \(y<z\),收益就是 \(|a_x-a_y|-S_y+S_z\)。这个东西可以根据 \(a_x\) 和 \(a_y\) 的大小关系用数据结构维护。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f, N = 5e5 + 5;
int n, m, a[N], b[N], nxt[N];
long long sum[N], ans, del;
bool vis[N];
set<int> s;
map<int, int> lst;
struct BIT {
    long long c[N];
    BIT() { memset(c, -63, sizeof c); }
    void add(int x, long long v) {
        for (int i = x; i <= n; i += i & -i) {
            c[i] = max(c[i], v);
		}
    }
    long long query(int x) {
        long long ret = -INF;
        for (int i = x; i; i -= i & -i) {
            ret = max(ret, c[i]);
		}
        return ret;
    }
} tr1, tr2;
int main() {
    freopen("sequence.in", "r", stdin);
    freopen("sequence.out", "w", stdout);
	scanf("%d", &n);
    for (int i = n; i; i--) {
        sum[i] = sum[i + 1] + i;
    }
    for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
        if (!lst[a[i]]) {
            ans += sum[i];
            vis[i] = 1;
            b[++m] = a[i];
        } else {
            nxt[lst[a[i]]] = i;
        }
        lst[a[i]] = i;
        nxt[i] = n + 1;
    }
    sort(b + 1, b + m + 1);
    s.insert(-INF);
	s.insert(INF);
    for (int i = 1; i <= n; i++) {
        if (vis[i]) {
            set<int>::iterator it = s.lower_bound(a[i]);
            del = min(del, -sum[i] + sum[nxt[i]] + *it - a[i]);
            del = min(del, -sum[i] + sum[nxt[i]] - *(--it) + a[i]);
            s.insert(a[i]);
        }
    }
    for (int i = n; i; i--) {
        if (vis[i]) {
            int pos = lower_bound(b + 1, b + m + 1, a[i]) - b;
            del = min(del, sum[nxt[i]] + a[i] - tr1.query(pos));
            del = min(del, sum[nxt[i]] - a[i] - tr2.query(n - pos + 1));
            tr1.add(pos, sum[i] + a[i]);
            tr2.add(n - pos + 1, sum[i] - a[i]);
        }
	}
    printf("%lld", ans + del);
    return 0;
}

摆放鞋子

考虑每个鞋子会有一个权值,左上右下为0123,那么总是可以在总权值模四不变的情况下对于图做调整。

考虑直接根据左右鞋子的关系跑出来一个二分图匹配。

然后如果设答案是 \(ans\)。在不是完美匹配的情况下,答案就是 \(ans\)。否则,我们看看匹配之后的图是不是和之前的权值和一样,然后不一样就是搞一个答案减一。

标签:std,NOIP,int,sum,long,Day1,del,tot,CLYZ
来源: https://www.cnblogs.com/siriehn-nx/p/15363902.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有