zoukankan      html  css  js  c++  java
  • 2021.10.20CSP模拟模拟赛 赛后总结

    意想不到的骗分……

    首先开 (T1),怎么又是算期望的……不会……先跳过。

    (T2),emm……似乎是一道 (dp), 套路的设出 (dp[i][j]) (分别表示前 (i) 和前 (j) 个字符)状态,然后用 (KMP) 优化一下?

    但是不会转移方程,先跳过。

    (T3),咦?

    60% 的数据:n,m <= 30,q <= 10

    暴力似乎很可做的样子,写一发吧,不写怕不是要保龄。

    1.5h后……

    终于写完了,测一下样例,哎,过了,可以。大样例……算了,不测了,反正也是 (T)

    (T4),这是什么题啊……我只会 (n = 1) 的做法 (QwQ)

    于是再回去看 (T2),列了列式子,然后教练叫我们出去做核酸检测,趁机与机房大佬们交流了一下考试题目,人均 200+,心态小崩。

    半个小时后,回机房了,经过房神的指点继续写 (T2),然而还是不会……最后写了个 (KMP) 板子,输出了一下短串在长串中出现的次数,摸了。

    然后再去看 (T1),但是这个期望到底 tmd 怎么算啊。

    随便写了个深搜,统计了一下次数,乘了个 (n) 的逆元,摸了。

    最后打 (T4) 的暴力,那么直接计算一下 (n = 1) 时的答案为 (ans_1),假设 (n) 个点经过 (t) 时间后没有重叠,直接输出 (n * ans_1),结果还忘取模了,似乎挂了 10pts。

    考完了,去干饭,吃完饭后像往常一样回机房摸一会,lj 突然进来给我吓一跳,然后过来告诉我说:“你文件输入输出写错了。” 我:“???”

    看了一眼,发现 (T2) 确实写错了。

    然后 lj 又说:“我又给你测了一下,好像是 90 分左右,你看你考场上要是文件写错了……”(此处省略一顿教育)

    我:“90分???那岂不是说我 (KMP) 后输出了一下得了 90 分???”(震惊我一整年)

    事实证明数据确实水,好端端的一道 (AC) 自动机上 (dp) 变成了 (KMP) 贪心……

    预期: 10 + ? + 60 + 10 = 80pts

    事实上:10 + 90 + 80 + 25 = 205pts

    (CSP RP--)

    下面我们回归正题,简单来讲一下考试题。

    T1. F

    Solution

    首先我们发现环上的点选哪一个都是一样的,所以先 (Tarjan) 缩个点。

    然后跑一遍拓扑排序,找到有多少个点可以到达当前点。

    每个点被选后贡献就是 1,所以直接把被选的概率加在一起即可。

    答案即为 (sumlimits_{i = 1}^{n}{c_i})

    (c_i) 表示能到达点 (i) 的点的个数。

    还要用 (bitset) 维护一下,这样就可以直接求出 (c) 了。

    实践复杂度 (O(frac{n^3}{ω}))

    code
    dddddddddddd#include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <bitset>
    #include <queue>
    #define ll long long
    
    using namespace std;
    
    const ll mod = 998244353;
    const ll N = 1010;
    char s[N];
    ll n;
    vector <ll> g[N], G[N];
    bitset <N> vis[N];
    ll dfn[N], low[N], tim;
    ll stk[N], top, t[N];
    ll scc[N], cnt;
    ll in[N];
    
    inline void tarjan(ll x){
        dfn[x] = low[x] = ++tim;
        stk[++top] = x;
        t[x] = 1;
        for(auto y : g[x]){
            if(!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
            else if(t[y]) low[x] = min(low[x], dfn[y]);
        }
        if(low[x] == dfn[x]){
            cnt++;
            ll k;
            do{
                k = stk[top--];
                scc[k] = cnt;
                t[k] = 0;
                vis[cnt][k] = 1;
            }while(top && x != k);
        }
    }
    
    inline ll power(ll x, ll b){
        ll res = 1;
        while(b){
            if(b & 1) res = res * x % mod;
            x = x * x % mod;
            b >>= 1;
        }
        return res;
    }
    
    inline void topo(){
        queue <ll> q;
        for(ll i = 1; i <= n; i++)
            if(!in[i]) q.push(i);
        while(!q.empty()){
            ll x = q.front();
            q.pop();
            for(auto y : G[x]){
                vis[y] |= vis[x];
                if((--in[y]) == 0) q.push(y);
            }
        }
        return;
    }
    
    signed main(){
        scanf("%lld", &n);
        for(ll i = 1; i <= n; i++){
            scanf("%s", s + 1);
            for(ll j = 1; j <= n; j++)
                if(s[j] == '1') g[i].push_back(j);
        }
        for(ll i = 1; i <= n; i++)
            if(!dfn[i]) tarjan(i);
        for(ll i = 1; i <= n; i++)
            for(auto j : g[i])
                if(scc[i] != scc[j])
                    G[scc[i]].push_back(scc[j]), in[scc[j]]++;
        // cout << "topi" << endl;
        topo();
        ll ans = 0;
        for(ll i = 1; i <= n; i++)
            ans = (ans + power(vis[scc[i]].count(), mod - 2)) % mod;
        printf("%lld
    ", ans);
        return 0;
    }
    

    T2. S

    Description

    给出 (S)(T) 两个字符串,问至少删去 (S) 中多少个字符,才能使得 (T) 不在 (S) 中出现 即不存在 (l)(r) 使得 (S_{l∼r})=T

    Solution

    (AC) 自动机上 (dp)

    我们先对 (T)(trie) 图。

    (f[i][j]) 表示已经匹配到 (S) 的前 (i) 位,(T) 的前 (j) 位时,最多保留的字母个数。

    如果 (S) 的下一位不是 (T) 的终点,那么可以从 (f[i - 1][j] + 1) 转移过来。

    如果第 (j) 位不是 (T) 的终点,可以从 (f[i - 1][j]) 转移过来。

    code
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    
    const int N = 8010;
    char s[N], t[N];
    int n, m, ans;
    int trie[N][26], tot, fail[N];
    int f[N][N], End[N];
    
    inline void insert(char s[]){
        int now = 0;
        for(int i = 1; i <= m; i++){
            int c = s[i] - 'a';
            if(!trie[now][c]) trie[now][c] = ++tot;
            now = trie[now][c];
        }
        End[now] = 1;
    }
    
    inline void build(){
        queue <int> q;
        for(int i = 0; i < 26; i++)
            if(trie[0][i])
                q.push(trie[0][i]);
        while(!q.empty()){
            int now = q.front();
            q.pop();
            for(int i = 0; i < 26; i++){
                if(trie[now][i]){
                    fail[trie[now][i]] = trie[fail[now]][i];
                    q.push(trie[now][i]);
                }else trie[now][i] = trie[fail[now]][i];
            }
        }
    }
    
    int main(){
        // freopen("s.in", "r", stdin);
        // freopen("s.out", "w", stdout);
        scanf("%s%s", s + 1, t + 1);
        n = strlen(s + 1), m = strlen(t + 1);
        insert(t);
        build();
        for(int i = 1; i <= n; i++){
            for(int j = 0; j <= m; j++){
                if(!End[trie[j][s[i] - 'a']]) f[i][trie[j][s[i] - 'a']] = max(f[i][trie[j][s[i] - 'a']], f[i - 1][j] + 1);
                if(!End[j]) f[i][j] = max(f[i][j], f[i - 1][j]);
            }
        }
        int ans = 0;
        for(int i = 0; i <= tot; i++)
            ans = max(ans, f[n][i]);
        printf("%d
    ", n - ans);
        return 0;
    }
    /*
    abbabbab
    ab
    */
    

    T3. Y

    Description

    P1979 [NOIP2013 提高组] 华容道

    Solution

    暴力做法就是记录一下空格及起点,注意两个点都要记录。

    每次暴力向空格的四个方向搜索,如果空格不在起点四周,那么就直接令空格的四个方向的点入队。

    如果空格在起点四周,交换空格和起点入队。

    下面是正解:

    我们发现,暴力 (bfs) 时会有许多无用的状态,就是在空格向起点方向移动时,会向许多不优的点移动。

    所以可以优化。

    我们把每个点的四个方向重新标号,然后向四方连边,跑最短路。

    code
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int N = 35;
    int n, m, q, ans;
    int a[N][N];
    struct node{
        int x, y;
    }e, s, t;
    struct Edge{
        int v, w, nxt;
    }edge[20010];
    int head[5010], tot;
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    bool vis[5010];
    int d[N][N], id[N][N][5];
    int dis[5010];
    
    bool check(int x, int y){
        if(x < 1 || x > n || y < 1 || y > m || !a[x][y]) return 0;
        return 1;
    }
    
    inline void add(int x, int y, int z){
        edge[++tot] = (Edge){y, z, head[x]};
        head[x] = tot;
    }
    
    inline void bfs(int sx, int sy, int tx, int ty){
        memset(d, 0x3f, sizeof(d));
        queue <node> q;
        q.push((node){sx, sy});
        d[sx][sy] = 0;
        while(!q.empty()){
            node now = q.front();
            q.pop();
            for(int i = 0; i < 4; i++){
                int mx = now.x + dx[i];
                int my = now.y + dy[i];
                if(mx == tx && my == ty) continue;
                if(check(mx, my)){
                    if(d[mx][my] > d[now.x][now.y] + 1){
                        d[mx][my] = d[now.x][now.y] + 1;
                        q.push((node){mx, my});
                    }
                }
            }
        }
    }
    
    inline void spfa(int sx, int sy){
        memset(dis, 0x3f, sizeof(dis));
        memset(vis, 0, sizeof(vis));
        queue <int> q;
        for(int i = 0; i < 4; i++){
            int mx = sx + dx[i];
            int my = sy + dy[i];
            if(check(mx, my)){
                dis[id[sx][sy][i]] = d[mx][my];
                q.push(id[sx][sy][i]);
                vis[id[sx][sy][i]] = 1;
            }
        }
        while(!q.empty()){
            int x = q.front();
            q.pop();
            vis[x] = 0;
            for(int i = head[x]; i; i = edge[i].nxt){
                int y = edge[i].v;
                if(dis[y] > dis[x] + edge[i].w){
                    dis[y] = dis[x] + edge[i].w;
                    if(!vis[y]) vis[y] = 1, q.push(y);
                }
            }
        }
    }
    
    int main(){
        // freopen("y.in", "r", stdin);
        // freopen("y.out", "w", stdout);
        scanf("%d%d%d", &n, &m, &q);
        for(int i = 1, tot = 0; i <= n; i++)
            for(int j = 1; j <= m; j++){
                scanf("%d", &a[i][j]);
                for(int k = 0; k < 4; k++)
                    id[i][j][k] = ++tot;
            }
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++){
                if(!a[i][j]) continue;
                for(int k = 0; k < 4; k++){
                    int mx = i + dx[k];
                    int my = j + dy[k];
                    if(check(mx,my))
                        add(id[i][j][k], id[mx][my][k ^ 1], 1);
                }
                for(int k = 0; k < 4; k++){
                    int nx = i + dx[k];
                    int ny = j + dy[k];
                    if(check(nx, ny)){
                        bfs(nx, ny, i, j);
                        for(int l = 0; l < 4; l++){
                            if(k == l) continue;
                            int mx = i + dx[l], my = j + dy[l];
                            if(check(mx, my)) add(id[i][j][k], id[i][j][l], d[mx][my]);
                        }
                    }
                }
            }
        while(q--){
            scanf("%d%d%d%d%d%d", &e.x, &e.y, &s.x, &s.y, &t.x, &t.y);
            // cout << e.x << " " << e.y << " " << s.x << " " << s.y << " " << t.x << " " << t.y << endl;
            if(s.x == t.x && s.y == t.y){
                puts("0");
                continue;
            }
            bfs(e.x, e.y, s.x, s.y);
            spfa(s.x, s.y);
            int ans = 1e9;
            for(int i = 0; i < 4; i++){
                int mx = t.x + dx[i];
                int my = t.y + dy[i];
                if(check(mx, my)) ans = min(ans, dis[id[t.x][t.y][i]]);
            }
            if(ans == 1e9) puts("-1");
            else printf("%d
    ", ans);
        }
        return 0;
    }
    

    T4. F

    一道 (cf) 3500 分的题,完全不会做,我还是太菜了 (QwQ)

    本文来自博客园,作者:xixike,转载请注明原文链接:https://www.cnblogs.com/xixike/p/15432797.html

  • 相关阅读:
    27、springboot整合RabbitMQ(1)
    26、springboot与消息
    25、springboot与缓存整合Redis
    24、springboot与缓存(2)
    linux之参数实用讲解
    linux之创建临时文件的方法
    【转】linux之shfit
    linux之stat
    Shell 环境中的输入输出重定向
    Linux下samba的安装与配置
  • 原文地址:https://www.cnblogs.com/xixike/p/15432797.html
Copyright © 2011-2022 走看看