Codeforces Round 966 (Div. 3) 题解

A

人类检测题,直接判断前两位和后一位加长度即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    string a; cin >> a;
    int pre = (a[0]-'0')*10 + (a[1]-'0');
    if (pre != 10) { cout << "NO\n"; return; }
    int num = (a[2]-'0');
    if (num >= 2) { cout << "YES\n"; return; }
    else if (num == 1 && a.size() >= 4) { cout << "YES\n"; return; }
    cout << "NO\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

B

纯模拟。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <int> a(n);
    for (int i = 0;i < n;i++) cin >> a[i];
    bool flag = true;
    vector <bool> seat(n+1);
    for (auto node : a) {
        if (flag) seat[node] = true, flag = false;
        else {
            if (node == 1) {
                if (!seat[node+1]) { cout << "NO\n"; return; }
            }else if (node == n) {
                if (!seat[node-1]) { cout << "NO\n"; return; }
            }else if (node > 1 && node < n && !seat[node+1] && !seat[node-1]) { cout << "NO\n"; return; }
            seat[node] = true;
        }
    }
    cout << "YES\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

C

这题本意就是让一种字母对应一种数字,直接两个 map 扔过去就行了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <ll> a(n+1);
    for (int i = 0;i < n;i++) cin >> a[i];
    int m; cin >> m;
    while (m--) {
        string use; cin >> use;
        map <int, int> mp;
        map <int, char> mp2;
        if (use.size() != n) { cout << "NO\n"; continue; }
        int pos = 0; bool flag = true;
        for (auto i : use) {
            if (mp.find(i-'a') == mp.end()) mp[i-'a'] = a[pos];
            else {
                if (mp[i-'a'] != a[pos]) { flag = false; break; }
            }

            if (mp2.find(a[pos]) == mp2.end()) mp2[a[pos]] = i; 
            else {
                if (mp2[a[pos]] != i) { flag = false; break; }
            }
            pos++;
            // if (cnt[i-'a'].size() >= 2) { flag = false; break; }
        }
        if (flag) { cout << "YES\n"; continue; }
        else cout << "NO\n";
    }
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

D

思路历程

想的贪心,发现不仅要区间越大越好,还要区间越多越好。

于是想到从小区间开始往大区间走,发现会遗漏许多本该加上的数字,于是从大区间开始贪心,发现只需要尽可能地选择大的区间即可,操作时我们反着来即可。这里用两个优先队列维护 \(l\) 从小到大和 \(r\) 从大到小即可,然后前缀和一下就行了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long

void solve () {
    int n; cin >> n;
    vector <ll> a(n+1), sum(n+1);
    for (int i = 1;i <= n;i++) {
        cin >> a[i];
        sum[i] = sum[i-1] + a[i];
    }
    string s; cin >> s;
    priority_queue <int, vector <int>, greater <int>> q;
    priority_queue <int, vector <int>, less <int>> q2;
    
    for (int i = 0;i < s.size();i++) {
        if (s[i] == 'L') q.push(i+1);
        else q2.push(i+1);
    }
    ll ans = 0;
    while (!q.empty() && !q2.empty()) {
        int l = q.top(), r = q2.top();
        q.pop(), q2.pop();
        if (l > r) break;
        ans += sum[r] - sum[l-1];
    }
    cout << ans << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

E

总结:乘法原理+排序

不难由贪心想到,对于每一个数字而言,应该是越大的就要放在出现次数越多的矩形里。那么现在难点就是去记录每个点的出现的矩形数量即可。

我们可以记录一下能覆盖当前点 \((i, j)\) 的区间出现的地方,不难发现一个单位位置可以出现一个矩形,所以我们设 \(lex = \max(0, i-k+1) \ rix = \max(i, n-k)\),那么 \(y\) 轴也是同理。

前边我们说了,一个点就是一种矩形出现的位置,所以我们设 \(cnt_{i, j}\) 为一个点被覆盖的区间数量,则为 \(cnt_{i, j} = (rix-lex+1)\times (riy-ley+1)\)

最后就是分别排个序,然后大 \(\times\) 大就行了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 2e5+5;

ll ans[N];

void solve () {
    int n, m, k; cin >> n >> m >> k;
    int w; cin >> w;
    vector <int> a(w);
    for (int i = 0;i < w;i++) cin >> a[i];
    int cnt=0;
	for(int i=0;i<n;i++){
		for(int j = 0;j<m;j++){
			int lex=max(0,i-k+1);
			int rix=min(i,n-k);
			int ley=max(0,j-k+1);
			int riy=min(j,m-k);
			ans[cnt]=(rix-lex+1)*(riy-ley+1);
			//cout<<ans[cnt]<<"	"; 
			cnt++;
		}
	}
	long long res=0;
	int i=0;
	sort(a.begin(),a.end(),[&](int x, int y) { return x > y; });
	sort(ans,ans+cnt, [&](int x, int y) { return x > y; });
	while(w>0 && cnt>=0){
		//cout<<a[i]<<"	"<<ans[i]<<endl;
		res+=a[i]*ans[i];
		i++;
		w--;cnt--;
	}
	cout<<res<<endl;
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}

F

很好的 dp 题,使我大脑旋转。

我们不难发现,这道题因为每个矩形补全的不同,是不太能用贪心的,所以我们考虑一下 dp

考虑 \(dp_{i, j}\) 为考虑到第 \(i\) 个矩形可以得到 \(j\) 分,发现一个矩形它最多可以贡献 \(x + y\) 分。(\(x\) 为宽,\(y\) 为高)这里给出证明:假设有 \(x < y\),那么根据贪心我们肯定是先涂色长度为 \(x\) 的宽,那么我们可以先得到 \(x\) 得分,此时剩余的矩形变成了高为 \(x\) 宽为 \(y-x\) 的矩形,那么按照贪心我们假设 \(y-x < x\),那么我们先涂 \(y-x\) 可以得到 \(x-1\) 的得分,最后剩下高为 \(1\) 的矩形,而宽为 \(y-x\),所以得分又要加上 \(y-x\),但是因为只剩最后一个方格时,填色会让列和行同时满足,所以再 \(+1\)

那么我们不去细想分裂的方式,我们直接去想得分的情况,我们可以粗略得到这个转移式子:

\[dp_{i+1, j+j_1} = \min(dp_{i+1, j+j_1}, \ dp_{i, j_1} + cost) \]

这里的 \(j\) 就是我们先前求出的状态,\(j_1\) 就是我们现在枚举的状态,也就是我们想要在第 \(i\) 个矩形得到的分数,而且因为达到 \(x+y\)\(x+y-1\) 的步数其实是一样的,所以我们可以直接集成在 \(x+y\) 里。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair <int, int>

void solve () {
    int n, k;
    cin >> n >> k;
    // int tmp = n;
    vector<pii> m(n);
    for (int i = 0;i < n;i++) cin >> m[i].first >> m[i].second;
    vector<vector<int>> dp(n+1, vector<int>(k+1, 1e9));
    dp[0][0] = 0;
    int i = 0;
    for (i = 0;i < n;++i) {
        int x = m[i].first, y = m[i].second;
        int sum = x + y, cost = 0;
        for (int j = 0;j <= sum;j++) {
            for (int j1 = 0;j1 + j <= k;j1++) { 
                dp[i+1][j1+j] = min(dp[i+1][j1+j], dp[i][j1] + cost); 
            }
            if (j < sum) {
                if (x >= y) x--, cost += y;
                else y--, cost += x;
            }
        }
    }
    cout << (dp[n][k] == 1e9 ? -1 : dp[n][k]) << "\n";
}

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int _ = 1; cin >> _;
    while (_--) solve();
    return 0;
}