2024“钉耙编程”中国大学生算法设计超级联赛(10)

时间:08_18

  • 1011 NOI2024 31.80% (703/2211)
  • 1008 SunBian 55.02% (669/1216)
  • 1009 不基本子串结构 20.57% (589/2864)
  • 1002 scenery 21.00% (368/1752)

1011 NOI2024

思路

题目问的是“是否一定”,考虑最差情况,比自己排名高的全部拿分了,剩下的人一分不拿,与自己并列排名

最后每场比赛在自己排名之前的还是在自己排名前

尽量使每场比赛比自己强的人不同

代码

#define rep(i, j, k) for (int i = (j); i < (k); i++)
signed main() {
    IOS;
    int t, n, m, k, hcnt = 0, temp, a;
    cin >> t;
    while (t--) {
        hcnt = 0;
        cin >> n >> m >> k;

        rep(i, 0, n) {
            cin >> a;
            hcnt += a - 1;
        }

        rep(i, 0, n) cin >> temp;
        if (hcnt >= m) hcnt = m - 1;
        if (hcnt + 1 <= k) {
            cout << "YES" << endl;
        } else {
            cout << "NO" << endl;
        }
    }
    return 0;
}

1008 SunBian

思路

一开始是个环,那么Alice操作,把环断开,变成一条线

现在游戏有点像一个下棋问题(忘记具体叫什么了)

\(n×m\) 的棋盘上下棋,轮流下棋(还是放硬币来着),谁先放不下就输,让你判断自己先手能赢还是后手能赢

本题策略就是,在环断开后,再将线从中间断开,接下来相当于一人一条链,跟对方的处理一样能保证自己赢

也就是,A把环变链,B把链对等切开,然后B能稳赢

\(k=1\)\(k>=n\) 特判一下

代码

signed main() {
    IOS;
    long long t, k, n;
    cin >> t;
    while (t--) {
        cin >> n >> k;
        if (k == 1)
            if (n % 2 == 1)cout << "A";
            else cout << "B";
        else if (k >= n)cout << "A";
        else cout << "B";
    }
    return 0;
}

1009 不基本子串结构

思路

分类

  1. A是B的子串
    • A只在B中出现一次,答案是B的长度
    • A在B中出现不止一次,答案是-1
  2. 两者没有子串关系
    • 取最长的公共的A的后缀和B的前缀或是A的前缀和B的后缀
      • 例:ABBAB和BBABAA,那么最长的满足要求的串就是BBAB
      • 则构造出的最长串为 A-BBAB-AA

取前缀或后缀用KMP或者哈希,题解表示哈希方法不能用ull自然溢出

代码

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

#define YES "Yes"
#define NO "No"
#define F first
#define S second
#define int long long
#define ull unsigned long long
#define endl "\n"
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define rep(i, j, k) for (int i = (j); i < (k); i++)
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define pii pair<int, int>

int MOD = 100000037;

int p = 100291;
ull ksm(ull a, ull b) {
    ull res = 1;
    while (b) {
        if (b & 1) res = (res * a) % MOD;
        a = (a * a) % MOD;
        b >>= 1;
    }
    return res;
}
int solve(string &s1, string &s2) {
    int n, m;
    n = s1.length();
    m = s2.length();
    if (n > m) { // 让s1为短的,s2为长的
        swap(n, m);
        swap(s1, s2);
    }

    // 检查s1是否是s2的子串
    if (s2.find(s1) != string::npos) {
        int sonindex = s2.find(s1);
        // 如果是子串,再检查剩余的是否有子串
        if (s2.substr(sonindex + 1, m - sonindex - 1).find(s1) != string::npos)
            return -1;
        else
            return m;
    } else { // 查s1的后缀和s2的前缀的最长相等串的长度以及s1的前缀和s2的后缀的最长相等串的长度,采用哈希法
        int l1 = 0, l2 = 0;
        ull h1 = 0, h2 = 0;
        for (int i = 0; i < n; i++) {
            (h1 += (s1[n - i - 1]) * ksm(p, i)) %= MOD;
            h2 = (h2 * p + s2[i]) % MOD;
            if (h1 == h2) l1 = i + 1;
        }
        h1 = 0, h2 = 0;
        for (int i = 0; i < n; i++) {
            h1 = (h1 * p + s1[i]) % MOD;
            (h2 += (s2[m - i - 1]) * ksm(p, i)) %= MOD;
            if (h1 == h2) l2 = i + 1;
        }
        return min(n + m - l1, n + m - l2);
    }
}

signed main() {
    IOS;
    int t, n, m;
    string s1, s2;
    cin >> t;
    while (t--) {
        cin >> s1 >> s2;
        cout << solve(s1, s2) << endl;
    }
    return 0;
}

1002 scenery

思路

题目给的数据是一个类似漏斗的形状,很容易能想到想要拍完所有景色,就尽量紧凑的选取时间段拍摄

一开始想的是从漏斗下面开始填充,称为第一层,那么第二层就是紧贴着第一层选择的时间段进行拍摄,要么从上往下,先把 \([l_i,r_i]\) 的左右填充了,往下层转移

不过没想到怎么转移,根据jls和题解的方式,\(dp[i][j]\) 表示 第 \(i\) 层的 \(j\) 时间到 \(dp[i][j]\) 是没被选择的

那么每次转移大致就是, \(j+t[i]\)\(dp[i-1][j]\)\(j\)\(dp[i-1][j]-t[i]\)

也就是 \(dp[i][j]=dp[i-1][j]-t[i],dp[i][j+t[i]]=dp[i-1][j]\)

然后考虑一下左右边界

代码

代码借鉴了jls的写法

#define rep(i, j, k) for (int i = (j); i < (k); i++)

const int N = 5010;
int l[N], r[N], t[N];
void solve() {
    int n, m;
    cin >> n >> m;
    rep(i, 0, n) cin >> l[i] >> r[i] >> t[i];

    vector<int> dp(m + 1, -1);
    dp[1] = m;
    for (int i = n - 1; i >= 0; i--) {
        vector<int> ndp(m + 1, -1);
        for (int j = 1; j <= m; j++) {
            if (dp[j] >= j) {
                int L = max(l[i], j);
                int R = min(r[i], dp[j]);
                if (R - L + 1 >= t[i]) {
                    ndp[L] = max(ndp[L], R - t[i]);// 取最优
                    ndp[L + t[i]] = max(ndp[L + t[i]], R);
                }
            }
        }
        dp = ndp;
    }
    for (int i = 1; i <= m; i++)
        if (dp[i] >= i - 1) return cout << "YES\n", void();
    cout << "NO\n";
}
posted @ 2024-08-23 15:56  lulaalu  阅读(108)  评论(0编辑  收藏  举报