Atcoder Beginner Contest 423

A

入机验证题。

答案 \(\lfloor{\frac{X}{1000 + C}}\rfloor\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
int X, C;

signed main() {
	scanf ("%lld %lld", &X, &C);
	printf ("%lld\n", X / (1000 + C) * 1000);
	return 0;
}

B

找前缀后缀 0 的个数即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a[105], cntl, cntr;

signed main() {
	scanf ("%lld", &n);
	for (int i = 1; i <= n; i++)
		scanf ("%lld", &a[i]);
	for (int i = 1; i <= n; i++) {
		if (!a[i])
			cntl++;
		else
			break;
	}
	for (int i = n; i >= 1; i--) {
		if (!a[i])
			cntr++;
		else
			break;
	}
	
	if (cntl + cntr > n)
		printf ("0\n");
	else
		printf ("%lld\n", n + 1 - cntl - cntr - 2);
	return 0;
}

C

最优的方法肯定是从 \(R\) 开始两边走到底来关,那么原本关着的要被操作两次,原本开着的只会被操作一次。

#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int MAXN = 5e5 + 10;
int n, R, lpos, rpos, ans, L[MAXN];

signed main() {
	scanf ("%lld %lld", &n, &R);
	for (int i = 1; i <= n; i++)
		scanf ("%lld", &L[i]);
		
	lpos = 0;
	for (; lpos < R; lpos++) {
		if (L[lpos + 1] == 0)
			break;
	}
	rpos = n;
	for (; rpos > R; rpos--) {
		if (L[rpos] == 0)
			break;
	}
	
	for (; lpos < R; lpos++) {
		if (!L[lpos + 1])
			ans++;
		else
			ans += 2;
	}
	for (; rpos > R; rpos--) {
		if (!L[rpos])
			ans++;
		else
			ans += 2;
	}
	
	printf ("%lld\n", ans);
	return 0;
}

D

我草我怎么老是拿道题大脑空白。

小学奥数之前学过一个很神奇的东西叫做最优统筹,同理可以考虑这题。具体的,人数不是问题,只要时间最短即可,那么按照用餐时间从小到大用优先队列维护,下一批人不够就弹出队头增加空位,如果当前时间没到下一批的时间就直接跳到下一批的进入时间即可,

#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
char buf[1 << 23], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
inline int read() {
    int res = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
        f = ch == '-' ? -1 : 1, ch = getchar();
    while (isdigit(ch))
        res = res * 10 + (ch ^ 48), ch = getchar();
    return res * f;
}

const int N = 3e5 + 10;

int n, k, tim;
int A[N], B[N], C[N];

priority_queue<pii, vector<pii>, greater<pii>> q;

signed main() {
    n = read(), k = read();
    for (int i = 1; i <= n; i++)
        A[i] = read(), B[i] = read(), C[i] = read();
    for (int i = 1; i <= n; i++) {
        while (k < C[i]) {
            pii t = q.top();
            q.pop();
            k += t.second;
            tim = t.first;
        }
        tim = max(tim, A[i]);
        printf ("%lld\n", tim);
        q.push(make_pair(B[i] + tim, C[i]));
        k -= C[i];
    } 
    return 0;
}

E

考虑每个 \(a_i\) 对答案的贡献,那么就需要可考虑包含其的区间个数。在 \(<i\) 的一边选择心仪的左端点,\(>i\) 的一边选择心仪的右端点,你发现总共有 \((r - i + 1) \times (i - l + 1)\) 个区间包含了 \(a_i\),那么对答案的贡献就是 \((r - i + 1) \times (i - l + 1) \times a_i\),拆开之后就是 \(a_i \times i \times (l + r) - a_i \times i^2 + a_i \times (r - l + 1 - lr)\),那么只需要记 \(a_i,a_i\times i^2, a_i \times i\) 的前缀和即可。

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

const int N = 3e5 + 10;
int n, q;
int a[N], sum1[N], sum2[N], sum3[N];

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum1[i] = sum1[i - 1] + a[i];
        sum2[i] = sum2[i - 1] + a[i] * i * i;
        sum3[i] = sum3[i - 1] + a[i] * i;
    }

    for (int _ = 1; _ <= q; _++) {
        int L, R; cin >> L >> R;
        cout << (sum3[R] - sum3[L - 1]) * (L + R) - (sum2[R] - sum2[L - 1]) + (sum1[R] - sum1[L - 1]) * (R - L + 1 - L * R) << '\n';
    }
    return 0;
}

F

看到恰好的第一时间脑袋空空???

其实第一时间没看到恰好两字(

考虑二项式反演,记 \(g_k\) 表示至少 \(k\)\(a_i\) 整除 \([1,Y]\) 内的同一个数的次数,\(f_k\) 表示恰好 \(k\)\(a_i\) 整除 \([1,Y]\) 内的同一个数的次数。

\[g_k = \sum\limits_{i = k}^{N}{\binom{i}{k}f_i} \\ \downarrow \\ f_k = \sum\limits_{i = k}^{N}{(-1)^{i - k}\binom{i}{k}g_i} \]

关键在于如何求 \(g_i\)

我们发现最终确定的年份与所选择的 \(a_i\) 的最小公倍有关,同时选择的 \(a_i\) 个数小于 \(m\) 一定没有贡献。那么求出最小公倍数 \(val\) 之后,如果 \(val \mid Y\),则至少就会有 \(m\)\(a_i\) 满足条件。但是求最小公倍的时候会爆 longlong,那么 \(val > Y\) 直接舎掉即可。现在我们可以来算 \(g_i\) 了:\(g_i = \lfloor{\frac{Y}{\operatorname{lcm}\limits_{x \in \left|S\right|}}}\rfloor\),那么最后有 \(ans = \sum{(-1)^{\operatorname{popcount(st) - m}}\binom{\operatorname{popcount(st)}}{m}g_i}\)

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

const int N = 25, S = (1 << 20) + 10;
int n, m, Y, ans, a[N], C[N][N];

void initCombineNum() {
    C[0][0] = 1;
    for (int i = 1; i <= 20; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= 20; j++)
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
    }
}

signed main() {
    scanf ("%lld", &n);
    scanf ("%lld", &m);
    scanf ("%lld", &Y);
    for (int i = 1; i <= n; i++)
        scanf ("%lld", &a[i]);
    initCombineNum();
    for (int st = 0; st < (1 << n); st++) {
        int popc = __builtin_popcountll(st), val = 1;
        if (popc >= m) {
            bool flag = true;
            for (int i = 1; i <= n; i++) {
                if ((st >> (i - 1)) & 1) {
                    int t = __gcd(a[i], val);
                    if (1.0 * val / t > 1.0 * Y / a[i]) {
                        flag = false;
                        break;
                    }
                    val = val / t * a[i];
                }
            }
            if (flag)
                ans += (((popc - m) & 1) ? -1 : 1) * C[popc][m] * (Y / val);
        }
    }
    printf ("%lld\n", ans);
    return 0;
}
posted @ 2025-10-09 21:36  xAlec  阅读(5)  评论(0)    收藏  举报