Atcoder Beginner Contest 赛后总结

ABC 407

赛时表现

00:02    速切 A 题

00:04    速切 B 题

00:08    速切 C 题

00:19    D 题写出正解,但看错范围(\(HW \le 20\) 看成了 \(H,W \le 20\)),手搓 \(H = W = 20\) 的样例后发现超时

00:41    尝试优化,写了一版非递归式的,仍然超时

00:46    E 题题目没读懂

01:11    F 题读完题,先写了一份单调队列的暴力代码,调试了 5 分钟

01:17    继续优化 D 题,没有思路,抱着试一试的态度交了一发,过了(写总结才发现题目读错了)

01:40    E 题没有思路,尝试优化 F 题,感觉可以单调栈加上一些计算来搞,但没有具体的思路

赛后反思

  1. D 题读题有误,把数据范围看错了,从而写出正解但没交

  2. E 题没有读懂题目,也没有思路

ABC 406

赛时表现

00:03    速切 A 题

00:06    速切 B 题

00:16    写出 C 题第一版做法,对凹凸都做一遍前后缀和,对于每个点通过前后面凹凸的位置计算答案,发现第三个样例算多了

00:20    debug 失败,选择先开 D 题

00:23    想到可以暴力维护,直接开写

00:37    写完,一发通过

00:46    看完 E 题,尝试打表找规律,失败

00:59    重构 C 题代码,仍然算重

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n,a[N];
long long ans;
int pre[N],suf[N];
void solve() {
    cin >> n;
    for(int i = 1;i <= n;i ++) {
        cin >> a[i];
    }
    pre[0] = 0,suf[n] = n;
    for(int i = 2;i < n;i ++) {
        pre[i] = pre[i - 1];
        if(a[i] > a[i - 1] && a[i] > a[i + 1]) {
            pre[i] = i;
        }
    }
    for(int i = n - 1;i > 1;i --) {
        suf[i] = suf[i + 1];
        if(a[i] < a[i - 1] && a[i] < a[i + 1]) {
            suf[i] = i;
        }
    }
    for(int i = 1;i <= n;i ++) {
        if(pre[i] == i) {
            int l = i,r = suf[i];
            int L = pre[i - 1],R = suf[suf[i] + 1];
            cout << l << " " << r << " " << L << " " << R << "\n";
            ans += 1LL * (l - L) * (R - r);
        }
    }
    cout << ans << "\n";
}
int main() {
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cout << fixed << setprecision(12);
    int t = 1;
//    cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}

01:11    E 题想到动态规划,开始写,最初状态 dp[第几位][几位是1][是否受限] = 数字和,开始写,写到转移方程时发现不好转移

01:23    C 题想到枚举位置,二分下一个位置,实现后发现二分出的位置不对,答案有问题

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n,a[N];
long long ans;
int pre[2][N];
int find1(int l,int r) {
    while(l <= r) {
        int mid = l + r >> 1;
        if(pre[0][mid] - pre[1][mid] >= 1 && pre[1][mid] - pre[1][l] >= 1) {
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }
    return r;
}
int find2(int l,int r) {
    while(l <= r) {
        int mid = l + r >> 1;
        if(pre[0][mid] - pre[1][mid] <= 1 && pre[1][mid] - pre[1][l] <= 1) {
            l = mid + 1;
        }
        else {
            r = mid - 1;
        }
    }
    return r;
}
void solve() {
    cin >> n;
    for(int i = 1;i <= n;i ++) {
        cin >> a[i];
    }
    for(int i = 2;i < n;i ++) {
        pre[0][i] = pre[0][i - 1],pre[1][i] = pre[1][i - 1];
        pre[0][i] += a[i] > a[i - 1] && a[i] > a[i + 1];
        pre[1][i] += a[i] < a[i - 1] && a[i] < a[i + 1];
    }
    for(int i = 1;i <= n;i ++) {
        int j = find1(i,n),k = find2(i,n);
        cout << i << " " << j << " " << k << "\n";
        ans += max(0,k - j);
    }
    cout << ans << "\n";
}
int main() {
//    freopen(".in","r",stdin);
//    freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cout << fixed << setprecision(12);
    int t = 1;
//    cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}

01:31    E 题想到使用两个 DP 数组,dp[第几位][几位是1][是否受限] = 数字个数dp2[第几位][几位是1][是否受限] = 数字和,开始写

01:39    没设初始状态,答案算少了,思考后得出要倒着推,极限写完(但是写的时候并没有意识到是数位DP)

// Code by iamsh

#include<bits/stdc++.h>
using namespace std;
const int mod = 998244353;
long long n,k,l;
vector<int> bit;
long long dp1[61][61][2];
long long dp2[61][61][2];
void solve() {
	cin >> n >> k;
	bit.clear();
	while(n) {
		bit.push_back(n & 1);
		n >>= 1;
	}
	reverse(bit.begin(),bit.end());
    int l = bit.size();
    vector<long long> w(l);
    for(int i = 0;i < l;i ++) {
    	w[i] = 1LL << l - i - 1;
    }
    memset(dp1,0,sizeof dp1);
    memset(dp2,0,sizeof dp2);
    dp1[l][k][0] = dp1[l][k][1] = 1;
    for(int p = l - 1;~p;p --) {
        for(int c = 0;c <= k;c ++) {
            for(int t = 0;t < 2;t ++) {
                long long nc = 0,ns = 0;
                for(int b = 0;b < 2;b ++) {
                    if((t && b > bit[p]) || b + c > k) {
                    	continue;
                    }
                    int nt = t && b == bit[p];
                    nc = (nc + dp1[p + 1][b + c][nt]) % mod;
                    ns = (ns + 1LL * b * w[p] % mod * dp1[p + 1][b + c][nt]) % mod;
                    ns = (ns + dp2[p + 1][b + c][nt]) % mod;
                }
                dp1[p][c][t] = nc,dp2[p][c][t] = ns;
            }
        }
    }

    cout << dp2[0][0][1] % mod << "\n";
}
int main() {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cout << fixed << setprecision(12);
	int t = 1;
	cin >> t;
	while(t --) {
		solve();
	}
	return 0;
}

赛后总结

  1. C 题没有想到正解思路(只想到了计算点是凹还是凸):处理一个点和前面点的高低关系,发现一定是高低高的顺序。

  2. 没有及时看 F 题,F 题直接树剖线段树树状数组就可以解决

posted @ 2025-05-20 16:06  iamsh  阅读(90)  评论(0)    收藏  举报