yuwj  

开干

1001

思路:
就是扫描找到合法第一个然后就能去更新了
细节还是很好玩的

代码:

/*
对称:由于矩阵扫描方式是完全对称的,所以结果也会相同

扫描思想:
枚举行,枚举列
对于每一行,去看看前1...j列是不是出现了K种数字
如果出现了k种,知道了这些数字的最小出现列的最大列,就是Y的最大可满足位置

这样对于每一行就拿到了合法的Y的总数
然后,再将所有行给统计就是答案了

注意,因为这里是(1,1) -> (x,y) 所以之前统计的种类数,是可以直接使用的
因此需要维护全局seen,就实现了要求,一旦某一行出现seen == k,那么之后的所有行都能开始更新ans了

f[t]:数字t出现的最左列
*/
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int T;
    for(cin>>T;T;T--){
        int N, M, K;
        cin >> N >> M >> K;
        vector<vector<int>> a(N+1, vector<int>(M+1));

        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= M; j++)
            {
                cin>>a[i][j];
            }
        }

        int seen = 0;
        ll ans = 0;
        vector<int> f(K+1, M+1);
        for(int i = 1; i <= N; i++)
        {
            for(int j = 1,t; j <= M; j++)
            {
                t = a[i][j];
                if(f[t]>j)
                {
                    if(f[t] == M+1) seen++;
                    f[t] = j;
                }
            }

            if(seen < K) continue;

            int ymin = 0;
            for(int t = 1; t <= K; t++) ymin = max(ymin, f[t]);
            ans += (M-ymin+1);
        }

        cout << ans << '\n';
    }
    return 0;
}

1002

思路:
【01背包】
选或不选,选哪些前缀?
重点(我的思维盲点):每次选只能选前缀,而且每次分配的背包大小都不固定,所以要最后枚举背包容量
定义dp[i][j]:前i道题目使用容量k的背包选择的最大次数
转移:先i,再j,最后k,01背包经典转移就完了

其实这个题目我脑子里面是前后两条线傻傻分不清楚,但是其实就是枚举背包容量再01背包选就行了,因为每次都必须选前缀,所以也只有前缀和的1次选取就行,没有花里胡哨的

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 105, MAXM = 5e3 + 10;
ll dp[MAXN][MAXM], ans, tot;

template <typename T> bool chkmin(T &x, T y) {return y < x ? x = y, 1 : 0;}
template <typename T> bool chkmax(T &x, T y) {return y > x ? x = y, 1 : 0;}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    int T, N, M, X;
    for(cin >> T; T; --T){
        cin >> N >> M >> X;
        vector<vector<ll>> S(N+1, vector<ll>(M+1)), A = S, preS = S, preA = S;
        tot = 0;
        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= M; ++j)
            {
                cin >> S[i][j] >> A[i][j];
                preS[i][j] = preS[i][j-1] + S[i][j];
                preA[i][j] = preA[i][j-1] + A[i][j];
                tot += A[i][j];
            }
        }

        memset(dp, 0, sizeof dp);
        ans = 0;

        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= M; ++j)
            {
                for(int k = 0; k <= X;++k)
                {
                    //初始化,压缩了一维,降维了,滚动数组
                    chkmax(dp[i][k], dp[i-1][k]);
                    if(k>=preS[i][j]) chkmax(dp[i][k], dp[i-1][k - preS[i][j]]+preA[i][j]);
                    chkmax(ans,dp[i][k]);
                }
            }
        }

        cout << tot - ans << '\n';
    }
    return 0;
}

1003

这个题目竟然没想出来,基础非常薄弱啊,连bfs网络图最短路都想不起来了
代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500 + 10;
int T, N, M, a[MAXN][MAXN], k, val;
bool vis[MAXN][MAXN];

int dx[4]{0,0,1,-1}, dy[4]{1,-1,0,0};

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    for(cin >> T; T; T--){
        memset(a,0,sizeof a);
        memset(vis,0,sizeof vis);

        cin >> N >> M;
        for(int i = 1;i <= N; i++)
        {
            cin >> k;
            for(int j = 0; j < k; j++)
            {
                cin >> val;
                a[i][val] = 1;
            }
        }

        queue<tuple<int,int,int>> q;
        for(int i = 1; i <= N; i++)
            if(!a[i][1]) q.push({i,1,1});
        
        while(!q.empty())
        {
            auto [x,y,d] = q.front();
            q.pop();

            if(vis[x][y]) continue;
            vis[x][y] = true;

            if(y == M){
                cout << d << '\n';
                break;
            }

            for(int k = 0; k < 4; ++k)
            {
                int nx = x + dx[k], ny = y + dy[k];
                if(nx<1||nx>N||ny<1||ny>M||a[nx][ny]||vis[nx][ny]) continue;
                q.push({nx,ny,d+1});
            }
        }
    }
    return 0;
}

1004

1005

思路:
将B中所有长度为n的字符串建字典树
然后在树上看能不能保留1就完了
但是炸了,所以另谋他法...

就是题解的暴力了?
让GPT实现了我的思路,不过是SAM,后缀数组...
我就直接挂了(又偷懒了)
代码:

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

struct SAM_Node {
    int nxt[2]{-1, -1}; // 出边,-1 表示不存在
    int link = -1;      // 后缀链接
    int len  = 0;       // 本状态能表示的最长子串长度
    int dp   = 0;       // 从该结点继续能再走多长(向下最长链)
};

struct SuffixAutomaton {
    vector<SAM_Node> st;
    int last;                       // 末尾状态

    explicit SuffixAutomaton(int maxLen = 1) { init(maxLen); }

    void init(int maxLen) {
        st.clear();
        st.reserve(maxLen * 2 + 2);
        st.push_back(SAM_Node());   // root = 0
        last = 0;
    }

    void extend(int c) {            // c ∈ {0,1}
        int cur = st.size();
        st.push_back(SAM_Node());
        st[cur].len = st[last].len + 1;

        int p = last;
        while (p != -1 && st[p].nxt[c] == -1) {
            st[p].nxt[c] = cur;
            p = st[p].link;
        }

        if (p == -1) {
            st[cur].link = 0;
        } else {
            int q = st[p].nxt[c];
            if (st[p].len + 1 == st[q].len) {
                st[cur].link = q;
            } else {
                int clone = st.size();
                st.push_back(st[q]);          // 复制 q
                st[clone].len = st[p].len + 1;

                while (p != -1 && st[p].nxt[c] == q) {
                    st[p].nxt[c] = clone;
                    p = st[p].link;
                }
                st[q].link = st[cur].link = clone;
            }
        }
        last = cur;
    }

    /* 计算每个结点还能再往下延伸的最长长度 */
    void build_dp() {
        int sz = st.size();
        vector<int> bucket; bucket.reserve(sz);
        for (int i = 0; i < sz; ++i) bucket.push_back(i);
        sort(bucket.begin(), bucket.end(),
             [this](int a, int b) { return st[a].len < st[b].len; });

        for (int idx = sz - 1; idx >= 0; --idx) {
            int v = bucket[idx];
            int best = 0;
            for (int c = 0; c < 2; ++c) {
                int to = st[v].nxt[c];
                if (to != -1) best = max(best, st[to].dp + 1);
            }
            st[v].dp = best;
        }
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int T;
    cin >> T;
    while (T--) {
        int N, M;
        string A, B;
        cin >> N >> M >> A >> B;

        /* ---------- 1. 建 SAM ---------- */
        SuffixAutomaton sam(M);
        for (char ch : B) sam.extend(ch - '0');
        sam.build_dp();

        /* ---------- 2. 贪心走最大 xor ---------- */
        int v = 0;          // 当前 SAM 结点
        int rem = N;        // 还剩多少位没走
        long long ones = 0; // 结果 1 的个数

        for (int i = 0; i < N; ++i) {
            int abit  = A[i] - '0';
            int want  = abit ^ 1;              // 想把这一位变成 1
            int to_w  = sam.st[v].nxt[want];
            if (to_w != -1 && sam.st[to_w].dp >= rem - 1) {
                v = to_w;                      // 可以,直接异或成 1
                ++ones;
            } else {
                int to_s = sam.st[v].nxt[abit];
                v = to_s;                      // 只能保持为 0
            }
            --rem;
        }
        cout << ones << '\n';
    }
    return 0;
}
posted on 2025-05-17 07:32  xiaowang524  阅读(10)  评论(0)    收藏  举报