Codeforces Round 1050 (Div. 4)

https://codeforces.com/contest/2148

A. Sublime Sequence

题意:给定一个整数 x 和 n,要求得到一个长度为 n 的序列,形如 x, -x, x, -x...... 求序列之和。

题解:长度为奇数时输出 x,长度为偶数时输出 0。

void slove()
{
    int x, n; cin >> x >> n;
    if (n & 1) cout << x << endl;
    else cout << 0 << endl;
} 

B. Lasers

题意:在一个二维平面,需要从 (0, 0) 到 (x, y)。水平方向有 n 个激光束,垂直方向有 m 个激光束。给出这些激光束在水平方向和垂直方向的截距。问需要穿过多少个激光束才能到达 (x, y)。约束:每一个截距都小于 x 和 y。

题解:输出 \(n + m\)

void slove()
{
    int n, m; cin >> n >> m;
    int x, y; cin >> x >> y;
    for (int i = 1; i <= n; i++) {
        int a; cin >> a;
    }
    for (int i = 1; i <= m; i++) {
        int a; cin >> a;
    }
    cout << n + m << endl;
} 

C. Pacer

题意:花费一个单位时间从体育馆的一边到另一边,用1/0表示两边,初始在0侧。每次到另一侧可以加 1 分,现在要求在 t 时刻在 x 处。满足这些条件的前提下的最大得分。

题解:在两个时刻之间运动,时间之差为 c,开始运动的位置处于 y,要求处于的位置为 pos。

c                    if (pos = y and c is even) or (pos != y and c is odd) 
c - 1                if (pos = y and c is odd) or (pos != y and c is even)
void slove()
{
    int n, m; cin >> n >> m;
    vector<PII> a(n + 5);
    for (int i = 1; i <= n; i++) {
        int x, y; cin >> x >> y;
        a[i] = {x, y};
    } 
    int res = 0;
    int t = 0, pos = 0;
    for (int i = 1; i <= n; i++) {
        int x = a[i]._1, y = a[i]._2;
        if (pos == y) {
            int c = x - t;
            if (c & 1) res += c - 1;
            else res += c;
        } else {
            int c = x - t;
            if (c & 1) res += c;
            else res += c - 1;
        }
        t = x;
        pos = y;
    }
    res += m - t;  // 最后还要跑完全部的时间
    cout << res << endl;
} 

D. Destruction of the Dandelion Fields

题意:一个收割机收割草坪,如果草坪为奇数开关收割机,开变关,关变开。每块草坪只可以收割一次,可以任意选择收割顺序,问可最大的草坪面积为多少。初始收割机为关闭。

题解:特判没有奇数,总贡献为0。存在奇数,累加所有偶数。将奇数排序,第一个收割最大的,选一个最小的切换状态,在选次大的......

 void slove()
{
    int n; cin >> n;
    vector<int> a(n + 5);
    vector<int> b;
    bool flg = false;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (a[i] & 1) {
            b.emplace_back(a[i]);
            flg = true;
        }
    }
    if (!flg) {
        cout << 0 << endl;
        return;
    }
    LL res = 0;
    for (int i = 1; i <= n; i++) {
        if ((a[i] & 1) == 0) res += a[i];
    }
    sort(b.begin(), b.end());
    int m = b.size();
    for (int i = 0, j = m - 1; i <= j; i++, j--) {
        res += b[j];
    }
    cout << res << endl;
} 

E. Split

题意:有k个集合,选择一个子数组,将这个子数组的元素加入到集合1中,其它元素可以添加进任意集合中。是否可以是的所有集合的元素个数相同。这样的子数组有多少个。

题解:对于任意一个元素来说,元素个数一定可以被k整除,否则不可能使得所有的集合元素个数相同。
对任意一个元素 x 来说,集合1中最大的数量为 \(\frac{cnt_x}{k}\) 。所以子数组中,每个元素的个数不能超过这个数量,即可满足条件。

使用滑动窗口,固定子数组的一个端点,移动即可。每一个满足条件的窗口,贡献为整个窗口的大小。

void slove()
{
    int n, k; cin >> n >> k;
    vector<int> a(n + 5), cnt(n + 5);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        cnt[a[i]]++;
    }
    for (int i = 1; i <= n; i++) {
        if (cnt[i] % k != 0) {
            cout << "0" << endl;
            return;
        }
        cnt[i] /= k;
    }
    vector<int> t(n + 5);
    LL res = 0;
    for (int i = 1, j = 1; j <= n && j >= i; j++) {
        t[a[j]]++;
        while (t[a[j]] > cnt[a[j]]) {
            t[a[i]]--;
            i++;
        }
        res += j - i + 1;
    }
    cout << res << endl;
}

F. Gravity Falls

题意:有 n 行元素,每行元素个数任意。现在可以将这 n 行元素任意交换顺序,下面的元素个数小于上面的元素个数,重力使得上面的元素掉下来,可以得到一个字典序最小的序列。(每行元素为 k 个。所有 k 的和不超过 2e5)

题解:元素个数最多的就是最后答案的长度,枚举这个最大长度,也就是列,枚举行,得到一个最小的元素,可能会有多行都有这个最小元素。判断出这些行中字典序最小的。字典序最小的行就可以到下面。

PS:注意判断一定是字典序最小的,元素相等,长度不等,长度小的字典序更小。

1 2 3 4
1 2 3 4 5 6
9 9 9 9 1 2

初始要选第一个,不然选择第二个会使得第三个没选。

const int N = 2e5 + 5;
vector<int> g[N];
bool check(int a, int b, int i) {
    int len = min(g[a].size(), g[b].size());
    for (; i < len; i++) {
        if (g[a][i] != g[b][i]) return g[b][i] < g[a][i];
    }
    return g[b].size() < g[a].size();
}
void slove()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {
        g[i].clear();
    }
    int maxv = 0;
    for (int i = 1; i <= n; i++) {
        int k; cin >> k;
        g[i].resize(k);
        for (int j = 0; j < k; j++) cin >> g[i][j];
        maxv = max(maxv, k);  // 得到最长的长度
    }
 
    int cnt = 0;
    vector<int> res(maxv);
    for (int i = 0; i < maxv; i++) {  // 枚举列
        vector<int> idx;
        idx.reserve(n);
        int minv = 1e9;
        for (int j = 1; j <= n; j++) {  // 枚举行,得到最小的元素
            if (i >= g[j].size()) continue;
            minv = min(minv, g[j][i]);        
        }
        for (int j = 1; j <= n; j++) {  // 最小元素对应的行
            if (i >= g[j].size()) continue;
            if (g[j][i] == minv) idx.emplace_back(j);        
        }
        int id = idx[0];
        for (int j = 1; j < idx.size(); j++) {  // 枚举备选行,得到最小的行
            int now = idx[j];
            if (check(id, now, i)) {
                id = now;
            }
        }
        for (int j = i; j < g[id].size(); j++) {  // 将这一行的元素加入答案
            res[cnt++] = g[id][j];
        }
        i = g[id].size() - 1;  // 在重新得到新的行
    }
    
    for (int i = 0; i < cnt; i++) cout << res[i] << " ";
    cout << endl;
} 

G. Farmer John's Last Wish

题意:给定一个数组,求前缀[1, n], gcd(1~k) > gcd(1~k+1)的最大的k。\(1\leq k\leq n-1\) 。前缀可以任意排列。

题解:对于任意序列,设 g = gcd(1~k), x 为任意元素。始终满足 \(g \geq gcd(g, x)\) 。现在需要的就是去掉等号。
g一定是序列的某一个因子,如果某个因子的数量不等于序列的长度,表示至少有一个元素没有这个因子,再加上这个元素的gcd一定不会等于。最大的位置就是这个因子的数量。

const int N = 2e5 + 5;
vector<int> divi[N];
void init() {
	for (int i = 1; i < N; i++) {
		for (int j = i; j < N; j += i) {
			divi[j].emplace_back(i);
		}
	}	
}
void slove()
{	
	int n; cin >> n;
	vector<int> a(n + 5);
	for (int i = 1; i <= n; i++) cin >> a[i];
	vector<int> cnt(n + 5);
	int res = 0, g = 0;
	for (int i = 1; i <= n; i++) {
		int x = a[i];
		for (int v : divi[x]) {
			cnt[v]++;
			if (cnt[v] != i) {
				res = max(res, cnt[v]);
			}
		}
		if (gcd(g, x) != g) {  // 可能x中没有对应的因子,在枚举的时候就不会包含所有的前面元素的因子集。
			res = max(res, cnt[g]);
		}
		g = gcd(g, x);
		cout << res << " ";
	}
	cout << endl;
} 
posted @ 2025-09-20 21:51  yairr  阅读(13)  评论(0)    收藏  举报