Codeforces Round 965 (Div. 2) ABCE1

A题:Find K Distinct Points with Fixed Center

题意

给定坐标xc和yc,要求给出k个坐标,使得其中点是给定的坐标

思路

明显地,我们只要从xc和yc向两边“扩散”即可

代码

inline void solve() {
     int x, y, k; cin >> x >> y >> k;
     vector<int> a, b;
     if (k & 1) cout << x << ' ' << y << endl;
     for (int i = 1; k - 2 * i >= 0; i ++ ) {
        cout << x - i << ' ' << y - i << endl;
        cout << x + i << ' ' << y + i << endl;
     }
	 return;
}

B题:Minimize Equal Sum Subarrays

题意

给定一个排列,要求你也构造一个排列。

使得两者在(l,r)上的总和相等的(l,r)对数最少

思路

l取1,r取n的时候,无可避免地,两者会相等。

但是,我们看到样例二

5

1 2 3 4 5

构造

2 3 4 5 1

对于前面4个无论怎么取l和r,我们构造的一定比给定的排列大,因此不可能相等

然后再考虑最后一个

最后一个,当且仅当l取1,r取n的时候可以相等,所以这个构造方法是最优解

代码

inline void solve() {
     int n; cin >> n;
     for (int i = 1; i <= n; i ++ ) {
        int x; cin >> x;
        x += 1;
        if (x > n) x -= n;
        cout << x << " \n"[i == n];
     }
	 return;
}

C题:Perform Operations to Maximize Score

题意

给定一个数组和k次操作次数,每次操作可以使得数组中你选定的元素值加上1.

问对于所有的元素ai,ai和去掉第i位后的数组中位数之和最大的是多少?

思路

可以明确知道的是,当我们用完所有操作次数后

一定是a[n] + a[n/2]取到最大的(已经sort过后)

也就是数组中最大的,和中位数

所以,我们可以分出两种情况

1.不变最大的,让中位数增大

2.变最大的,一直增大最大的

为什么就这两种情况呢?

第一种不过多解释

第二种,如果我们把1~n-1数组中的某个ai变成了最大的,再往上加,贡献能够始终加一,是其他方案中的最优解,因此可以覆盖掉其他的考虑

在第二种情况下,我们只要看其是否能加,加上k和中位数再比较即可(当然是选最大的)

而对于第一种,我们可以二分出最大的中位数

二分过程中,我们看cost和cnt

如果x可以是中位数

那么一定有对于前n-1个元素,一定有n-n/2个元素大于等于x(要包括上中位数),而且cost一定小于等于k

代码

inline void solve() {
     ll n, k; cin >> n >> k;
     vector<PLL> a(n + 1);
     for (int i = 1; i <= n; i ++ ) cin >> a[i].first;
     for (int i = 1; i <= n; i ++ ) cin >> a[i].second;
     sort(a.begin() + 1, a.end());
     function<bool(ll)> check = [&](ll x) {
        ll cnt = 0, cost = 0;
        for (int i = n - 1; i; i -- ) {
            if (a[i].first >= x) cnt += 1;
            else if (cnt < (n - n / 2) && a[i].second) cost += x - a[i].first, cnt += 1;
        }
        return (cnt >= n - n / 2) && cost <= k;
     };
     ll l = 0, r = 1e9 + 7, ans = 0;
     while (l + 1 != r) {
        ll mid = l + r >> 1;
        if (check(mid)) l = mid;
        else r = mid;
     }
     ans = max(ans, a[n].first + l);
     for (int i = n; i; i -- ) {
        if (a[i].second) {
            swap(a[n], a[i]);
            sort(a.begin() + 1, a.end() - 1);
            ans = max(ans, a[n].first + k + a[n / 2].first);
            break;
        }
     }
     cout << ans << endl;
	 return;
}

E1:E1. Eliminating Balls With Merging (Easy Version)

题意

(简化)给定一个数组a,每次可以选定i和j,删去两者中小的一个,另一个变成两者之和。

问最后剩下来的最后一个元素的情况有多少种?

 思路

对于i,如果它能够留下来,那么一定能够把其他n-1个元素消灭。

每次我们进行操作,帮i进行扩散,把i旁边所有小于a[i]的给消灭掉

然后更新a[i]的值

这样的时间复杂度一定是够的,如果一开始值是a[i],下一次进行扩散的值就一定大于等于2*a[i]

所以时间复杂度是log级别的,然后看是否可以扩散就看最大值即可,用st表维护

代码

const int maxm = 2e5 + 9;
ll st[maxm][30], a[maxm], lg2[maxm], pre[maxm];
int n, k;
inline void build() {
    for (int i = 2; i <= n; i ++ ) lg2[i] = lg2[i >> 1] + 1;
	for (int i = 1; i <= n; i ++ ) st[i][0] = a[i];
	for (int j = 1; (1 << j) <= n; j ++ ) {
		for (int i = 1; i + (1 << j - 1) <= n; i ++ ) {
			st[i][j] = max(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
		}
	}
}
inline int query(int l, int r) {
	int s =  lg2[r - l + 1];
	return max(st[l][s], st[r - (1 << s) + 1][s]);
}
inline void solve() {
     cin >> n >> k;
     for (int i = 1; i <= n; i ++ ) cin >> a[i], pre[i] = pre[i - 1] + a[i];
     build();
     int ans = 0;
     function<void(int)> work = [&](int x) {
        int l = x, r = x;
        ll cur = a[x];
        while (true) {
            int temp1 = l, temp2 = r;
            int r1 = n + 1;
            while (r + 1 != r1) {
                int mid = r + r1 >> 1;
                if (query(x, mid) <= cur) r = mid;
                else r1 = mid;
            }
            r1 = 0;
            while (r1 + 1 != l) {
                int mid = r1 + l >> 1;
                if (query(mid, x) <= cur) l = mid;
                else r1 = mid;
            }
            cur = pre[r] - pre[l - 1];
            if (l == temp1 && r == temp2) break;
        }
        if (l == 1 && r == n) ans += 1;
     };
     for (int i = 1; i <= n; i ++ ) work(i);
     cout << ans << endl;
	 return;
}

 

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值