abc211

C - chokudai 559

询问长为 \(1e5\) 的给定字符串中,子序列 chokudai 的数量

简单dp。目标子序列没有重复元素,非常方便

D - Number of Shortest paths 755

边权为1,问最短路数量。bfs最短路径数模板题

普通的bfs 普通的队列

E - Red Polyomino 1823

在 n×n 黑白矩阵中,问把某 k 个白格染成红色且红色区域连通的方案数。\(n,k\le 8\)

可以dp(轮廓线?)但太麻烦了。

容易发现状态数小于 \(C_{n^2}^k=4426165368\),呃,好多啊!但实际上的状态数(不知道咋数学证明)远小于此,爆搜就能过

注意每一步是整个棋盘的状态,而不是一格

值得学习的是,可以不记录搜过的状态,染红并dfs之后染黑禁掉这格即可,这样就保证了每个状态只搜一次。代码飞快,12ms

#include <bits/stdc++.h>
using namespace std;

const int dx[] = {0,0,-1,1}, dy[] = {-1,1,0,0};
int n, k, ans;
string g[8];

void dfs(int k) {
    if (!k) return ans++, void();
    
    vector<pair<int, int>> ve;
    
    for (int x = 0; x < n; x++) {
        for (int y = 0; y < n; y++) {
            if (g[x][y] == '.') {
                bool conn = false; //连通性 周围有红色才搜
                for (int i = 0; i < 4; i++) {
                    int xx = x + dx[i], yy = y + dy[i];
                    if (xx < 0 || xx >= n || yy < 0 || yy >= n)
                        continue;
                    if (g[xx][yy] == '@')
                        conn = true;
                }
                
                if (conn) {
                    g[x][y] = '@'; //染红
                    dfs(k - 1);
                    g[x][y] = '#'; //禁掉
                    ve.push_back({x, y});
                }
            }
        }
    }
    
    for (auto [x, y] : ve)
        g[x][y] = '.'; //这里要恢复现场
}

int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        cin >> g[i];
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (g[i][j] == '.') {
                g[i][j] = '@'; //染红
                dfs(k - 1); //已经染了一个了
                g[i][j] = '#'; //已经搜过g[i][j]='@'的所有状态了,禁掉
            }
        }
    }
    
    cout << ans;
    
    return 0;
}

F - Rectilinear Polygons 2350

给定 \(n\) 个简单多边形(边界均水平或竖直,不一定凸)的所有顶点(输入的相邻顶点属于同一条边)和 \(q\) 个点,问每个点属于多少个多边形。范围和值域都是 \(1e5\)

一个简单的类似扫描线的东西,开个普通树状数组维护一列差分,实现单点 +1/-1 还有查询区间和,从左到右每次考虑一列即可

亮点:如何确定哪些点要 +1 哪些要 -1?画图发现每个多边形左下角(及在 x 最小的前提下 y 最小)的那个顶点必是 +1,其它点是 -1,+1,-1,+1,...

写了个 \([0,N-1]\) 的树状数组

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5;

int tr[N];
void add(int p, int v) {
    while (p < N) {
        tr[p] += v;
        p |= (p + 1);
    }
}
int ask(int p) {
    int s = 0;
    while (p >= 0) {
        s += tr[p];
        p = (p & (p + 1)) - 1;
    }
    return s;
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    
    int n;
    cin >> n;
    vector<vector<pair<int, int>>> modify(N);
    while (n--) {
        int m;
        cin >> m;
        vector<pair<int, int>> corners(m);
        for (auto &[x, y] : corners)
            cin >> x >> y;
        auto p = min_element(corners.begin(), corners.end()) - corners.begin(); //左下角的顶点
        for (int i = 0; i < m; i++) {
            auto [x, y] = corners[(p + i) % m];
            modify[x].push_back({y, i % 2 ? -1 : 1});
        }
    }
    
    int q;
    cin >> q;
    vector<vector<pair<int, int>>> points(N);
    for (int i = 0; i < q; i++) {
        int x, y;
        cin >> x >> y;
        points[x].push_back({y, i});
    }

    vector<int> ans(q);
    for (int x = 0; x < N; x++) {
        for (auto [y, v] : modify[x])
            add(y, v);
        for (auto [y, i] : points[x])
            ans[i] = ask(y);
    }
    
    for (auto x : ans) {
        cout << x << '\n';
    }
    
    return 0;
}

posted @ 2023-09-25 13:58  Bellala  阅读(37)  评论(0)    收藏  举报