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,所以直接把被选的概率加在一起即可。

答案即为 \(\sum\limits_{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\n", 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", 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\n", ans);
    }
    return 0;
}

T4. F

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

posted @ 2021-10-21 12:04  xixike  阅读(125)  评论(0编辑  收藏  举报