Loading

10.5 模拟赛

\(\rm NOIP\) 模拟赛

\(\rm Date: 10.5\)

上次真是太变态了,这次就平和许多

另外,不要在意accoders那丧心病狂的评测机速度了

今天4道都是CF原题

\(T1\) CF1252G

一看上去居然没思路啊,于是就把T2做了,再回来看这题终于发现了做法。其实我们只用维护当前有多少人的能力值比自己小,如果太小了就寄了。拿线段树维护区间加区间最小即可。

#include <bits/stdc++.h>
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#define rg register
#define emp emplace_back
using namespace std;
const int N = 1e5 + 5;

namespace Sg {
#define lsp p << 1
#define rsp p << 1 | 1
#define mid (t[p].l + t[p].r >> 1)
    struct T {
        int l, r, val; // Minimum
    }t[N << 2];
    int tag[N << 2]; // Num to add
    inline T operator + (T L, T R) {
        return {L.l, R.r, min(L.val, R.val)};
    }
    inline void pushup(int p) { t[p].val = min(t[lsp].val, t[rsp].val); }
    inline void fx(int p, int v) {
        t[p].val += v, tag[p] += v;
    }
    inline void pushdown(int p) {
        fx(lsp, tag[p]), fx(rsp, tag[p]), tag[p] = 0;
    }
    void build(int *arr, int p, int l, int r) {
        t[p] = {l, r}; if(l == r) return t[p].val = arr[l], void();
        build(arr, lsp, l, mid), build(arr, rsp, mid + 1, r); pushup(p);
    }
    void update(rg int p, rg int l, rg int r, rg int v) {
        if(l <= t[p].l && t[p].r <= r) return fx(p, v); pushdown(p);
        if(l <= mid) update(lsp, l, r, v); if(mid < r) update(rsp, l, r, v); pushup(p);
    }
    int query(int p, int l, int r) {
        if(l <= t[p].l && t[p].r <= r) return t[p].val; pushdown(p);
        if(r <= mid) return query(lsp, l, r); else if(l > mid) return query(rsp, l, r);
        else return min(query(lsp, l, r), query(rsp, l, r));
    }
}

int n, m, q, kk;
int b[N], c[N];
vector <int> a[N];

signed main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("Ans.txt", "w", stdout);
#endif
    scanf("%d %d %d", &n, &m, &q);
    for(int i = 0; i <= m; ++i) a[i].emp(0);
    for(int i = 1, x; i <= n; ++i) {
        scanf("%d", &x), a[0].emp(x);
        if(i == 1) kk = x;
        else {
            if(x < kk) ++b[0]; 
        }
    }
    //cout << kk << endl;
    c[0] = n;
    for(int i = 1, num; i <= m; ++i) {
        scanf("%d", &num);
        c[i] = num;
        for(int j = 1, x; j <= num; ++j) {
            scanf("%d", &x);
            a[i].emp(x);
            if(x < kk) ++b[i];
        }
    }
    //for(int i = 0; i <= m; ++i) cout << 
    //for(int i = 0; i <= m; ++i) cout << b[i] << ' ';
    //cout << endl;
    for(int i = 1; i <= m; ++i) b[i] = b[i - 1] - c[i] + b[i];
    for(int i = 0; i < m; ++i) b[i] -= c[i + 1];
    
    //for(int i = 0; i <= m; ++i) cout << b[i] << ' ';
    //cout << endl;
    
    Sg::build(b, 1, 0, m);
    for(int x, y, z; q--; ) {
        scanf("%d %d %d", &x, &y, &z);
        int val = a[x][y];
        //cout << a[x][y] << endl;
        if(val < kk && z > kk) Sg::update(1, x, m, -1);
        if(val > kk && z < kk) Sg::update(1, x, m, 1);
        if(Sg::t[1].val < 0) puts("0");
        else puts("1"); 
        a[x][y] = z;
        //printf(": %d\n", Sg::query(1, 2, 2));
    }
    return 0;
}

\(T2\) CF1515E

网上到处是 \(\mathcal{O(n^2)}\) 的做法还一个比一个奇怪,解释都不带解释的直接上柿子。哎,还是自己看得懂自己的解法

显然两端极长手开的电脑之间只会隔一个电脑,那么设 \(f[i, j]\) 表示考虑前 \(i\) 个电脑分 \(j\) 段手开,那么有转移:

\[f[i, j] = (\sum\limits_{x=1}^{i-2}f[i-x-1][j-1])\times C_{i-j+1}^x \times 2^{x-1} \]

\(\mathcal{O(n^3)}\),accoders上 \(100\to 25\)

#include <bits/stdc++.h>
#pragma GCC opimtize("Ofast")
#define rg register
using namespace std;
const int N = 405;
using LL = long long;
 
int n, mod;
// first i computers, j blocks -> total ways
int f[N][N];
LL jc[N], fjc[N], pw[N];
 
LL power(LL x, int y) {
    if(!y) return 1;
    LL t = power(x, y >> 1);
    return t * t % mod * (y & 1 ? x : 1) % mod;
}
 
inline void init() { 
    jc[0] = pw[0] = 1;
    for(int i = 1; i <= 400; ++i) jc[i] = 1LL * jc[i - 1] * i % mod;
    for(int i = 1; i <= 400; ++i) fjc[i] = power(jc[i], mod - 2);
    for(int i = 1; i <= 400; ++i) pw[i] = 2LL * pw[i - 1] % mod;
}
 
inline LL C(int x, int y) { // C(x, y) (x >= y)
    return jc[x] * fjc[y] % mod * fjc[x - y] % mod;
}

int g[N][N];
inline LL C_(int x, int y) {
    return g[x][y];
}
 
signed main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("Ans.txt", "w", stdout);
#endif
    cin >> n >> mod;
    init();
    //printf("%d\n", C(5, 3));
    f[1][1] = 1;
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            g[i][j] = C(i, j);
        }
    }
    for(rg int i = 2; i <= n; ++i) {
        f[i][1] = pw[i - 1];
        for(rg int j = 2; j <= n; ++j) {
            for(rg int x = 1; x < i - 1; ++x) {
                LL tmp = C_(i - j + 1, x) * pw[x - 1] % mod;
                //printf("i = %d, j = %d, tmp = %lld\n", i, j, tmp);
                //printf("from [%d, %d]\n", i - x - 1, j - 1);
                (f[i][j] += f[i - x - 1][j - 1] * tmp % mod) %= mod;
                //f[i][j] = (f[i][j] + )
                //f[i][j] = (f[i - x - 1][j - 1] + tmp) % mod;
            }
        }
    }
#if 0
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            printf("f[%d, %d] = %d\n", i, j, f[i][j]);
        }
    }
#endif
    LL ans = 0;
    for(int j = 1; j <= n; ++j) (ans += f[n][j]) %= mod;
    cout << ans;
    //cout << endl << clock();
    return 0;
}

\(T3\) CF1654F

当时发现一条性质就是 \(i\to i\ \text{xor}\ e\) 的时候 \(s_i\) 是有规律的交换的,然后luogu的题解就用了一个类似SA倍增的思路解决本题。

另一种思路就是预处理从 \(i\) 开始,变换 \(2^j\) 次的构成的字符串的哈希值(巧妙地利用了 \(i\to i\text{ xor }e\) 时“下一个字符”的变换规律),然后我们判断字典序就利用哈希值找到第一个不相同的字符的位置,从而判断两个生成串的字典序。

\(\mathcal{O(n\cdot2^n)}\)

#include <bits/stdc++.h>
#define rg register
#define BRUTE_FORCE 0
#define SOL 9
#pragma GCC optimize("Ofast")
using namespace std;
const int N = 3e5 + 5;

#if BRUTE_FORCE
string ret = "";
inline string Trans(string s, int val) {
    string ret = "";
    for(rg int i = 0; i < n; ++i) {
        ret += s[i ^ val];
        //cout << (i ^ val) << ' ';
    }
    //cout << endl;
    return ret;
}
inline char find(string s) {
    char ret = 'z';
    for(rg int i = 0; i < n; ++i) ret = min(ret, s[i]);
    return ret;
}
inline string solve(string s) {
    ret = Trans(s, 0);
    char ch = find(s);
    for(rg int i = 1; i < n; ++i) {
        if(s[i] != ch) continue;
        string tmp = Trans(s, i);
        if(ret > tmp) ret = tmp;
    }
    return ret;
}
#endif
#if SOL

using ULL = unsigned long long;
const ULL C[] = {998244353, ULL(1e9 + 7)};
ULL f[2][20][N];
ULL pw[2][N];
int n;
char s[N];

int cmp(int x, int y) {
    if(f[0][n][x] == f[0][n][y] && f[1][n][x] == f[1][n][y]) return 0;
    for(int j = n - 1; ~j; --j) 
        if(f[0][j][x] == f[0][j][y] && f[1][j][x] == f[1][j][y]) 
            x xor_eq 1 << j, y xor_eq 1 << j;
    return s[x] < s[y] ? -1 : 1;
}

void work() {
    cin >> n >> s;
    pw[0][0] = pw[1][0] = 1;
    for(int j = 0; j < 2; ++j) 
        for(int i = 1; i < (1 << n); ++i) pw[j][i] = pw[j][i - 1] * C[j];
    for(int i = 0; i < (1 << n); ++i) f[0][0][i] = f[1][0][i] = s[i] * s[i];
    for(int k = 0; k < 2; ++k) {
        for(int j = 1; j <= n; ++j) {
            for(int i = 0; i < (1 << n); ++i) 
                f[k][j][i] = f[k][j - 1][i] + f[k][j - 1][i ^ (1 << (j - 1))] * pw[k][1 << (j - 1)];
        }
    }
    int ans = 0;
    for(int i = 1; i < (1 << n); ++i) 
        if(cmp(ans, i) > 0) ans = i;
    for(int i = 0; i < (1 << n); ++i) putchar(s[ans ^ i]);
    return;
}

#endif

signed main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("Ans.txt", "w", stdout);
#endif
    work(); 
}

\(T4\) CF1710D

*3400 诶 有人当场A了orzycy

这是他的代码

大概思路就是从小到大考虑每个极长连通块,他必然包含了若干个子连通块区间。
设我们考虑到了好区间 \([l,r]\)\(l\) 所在连通块为 \([l',x_1 ]\)\(r\) 所在连通块为 \([x_k+1,r']\),它们之间还有 \(k-1\) 个连通块 \([x_i+1,x_{i+1}]\)。满足 $l'\le l\le x_1<...<x_k\le r \le r'l $
\(k\ge 1\)

容易发现 \(k=2\) 时无解。\(k=1\) 时直接连 \((l,r)\) 即可。\(k>2\) 时一种构造是连 \((l,r),(l,x_k)\) 以及 \((x_i,r)(2\le i<k)\)

不写了

#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 5;

int n, T, flg = 0;
int fa[N];
char s[N][N];

signed work() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) fa[i] = i, scanf("%s", s[i] + i); 
    for(int i = 1; i <= n; ++i) 
        for(int j = i - 1; j >= 1; --j) 
            if(s[j][i] == '1' && fa[i] > j) {
                printf("%d %d\n", j, i);
                if(fa[fa[i] - 1] > j) {
                    printf("%d %d\n", j, fa[i] - 1);
                    for(int k = fa[fa[i] - 1] - 1; k >= j + 1; --k) 
                        if(fa[k] == k) printf("%d %d\n", k, i);
                }
                for(int k = j; k <= i; ++k) fa[k] = fa[j];
            }
    return 0;
}

int main() {
    if(!T && !flg) {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("Ans.txt", "w", stdout);
#endif
        cin >> T, flg = 1;
    }
    if(!T && flg) return 0;
    work();
    --T;
    return main();
}
posted @ 2022-10-05 16:00  Into_qwq  阅读(46)  评论(3编辑  收藏  举报