Codeforces Round #816 (Div. 2)

[比赛链接](Dashboard - Codeforces Round #816 (Div. 2) - Codeforces)

B

题意:给定数组长度n,参数k,\(\sum_{1}^{n}a_i/k=b\),并且区间和是s。其中b和s都是题目给定的。

核心思路:这题我们可以抽象为一个被杯子倒水模型,就是我们先把所有的水,也就是s导入一个被子里面,如果s/k小于b,那么就肯定无解。因为我们接下来只有可能往每个被子里面导入k-1的水,使得这个值变小。因为\(\frac{k-1}{k}=0\).所以我们碰到问题得想考虑无解的情况。才好进一步思考。

但是我们要注意我们刚开始的s,可能减去k-1,它可以被整除,所以整体的值可能加1,就比如:10/3=3 9/3=3;

其实就是我们的第一杯水,随之值减小,可不可以被k-1整除的问题。下面的代码的地方很精髓。

接下来就可以直接上代码了

#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N = 200010;
int a[N];
void solve() {
    int n, k, b;
    LL s;
    std::cin >> n >> k >> b >> s;

    LL cur = s / k;
    if (b > cur) {
        std::cout << "-1\n";
        return;
    }

    std::vector<LL> a(n);
    a[0] = s;

    for (int i = 1; i < n; i++) {
        if (cur > b && a[0] >= k) {
            cur -= (a[0] % k != k - 1);
            a[0] -= k - 1;
            a[i] = k - 1;
        }
    }

    if (cur != b) {
        std::cout << "-1\n";
        return;
    }
    for (int i = 0; i < n; i++) {
        std::cout << a[i] << " \n"[i == n - 1];
    }
}
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		solve();
	}
}

C

题意:就是一个公式,不太好解释。

核心思路

  1. 首先我们需要把整个数组对应的值求出来,这个怎么求呢。就是我们可以从前往后地推。假设我们插入的a[i]!=a[i-1],那就说明所有包含a[i]的区间,他们的一个贡献值也就是区间的价值都会加一,而我们的区间数目就是区间中点的数目,这个自己模拟下就懂了,所以总体价值加i.但是如果相等,那么就只能加1,就是加上a[i]自己的长度。

  2. 接下来就只考虑把a[x]改为y的影响,很显然我们只需要比较a[x-1],a[x+1]与y的一个关系。下面来推导其中一种情况,如果刚开始i和i-1不相等,改完之后相等了,那么总体价值需要减去包含了a[x]的区间,这里一定要注意我们刚开始的总价值的推导。左边包含x的点:n-x+1,右边包含:x+1.接下来使用乘法原理就好了。i与i+1也是这么分析的。

这个题目我们可以多画图,某个区间包含某个点,我们可以以这个点为中心,向右或者向左延申一定长度的线段。

#include<bits/stdc++.h>
#include<unordered_map>
#define IOS std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef long long LL;
const int N = 2e5+10;
LL inf = (1LL << 30)-1 ;
typedef pair<int, int> PII;
LL a[N];
LL n, m;
void solve()
{
	cin >> n >> m;
	for (int i = 1;i <= n;i++)
		cin >> a[i];
	a[0] = -1, a[n+1] = -1;//这个只是单纯的为了放着相等的,因为a[i]都是大于0数
	LL ans = 1, suf = 1;//这是a[1]的贡献,所下面我们从i=2开始枚举,suf表示加入i的贡献
	for (int i = 2;i <= n;i++)
	{
		if (a[i] != a[i - 1])
		{
			suf += i;
			ans += suf;
		}
		else
		{
			suf += 1;
			ans += suf;
		}
	}
	while (m--)
	{
		LL x, y;
		cin >> x >> y;
		if (a[x] == y)
		{
			cout << ans << endl;
			continue;
		}
		if (y == a[x - 1] && a[x] != a[x - 1])
			ans -= (n - x + 1)*(x - 1);
		if (y != a[x - 1] && a[x] == a[x - 1])
			ans += (n - x + 1) * (x - 1);
		if (y == a[x+1] && a[x] != a[x + 1])
			ans -= (n - x) * (x);
		if (y != a[x+1] && a[x] == a[x + 1])
			ans += (n - x) * x;
		cout << ans << endl;
		a[x] = y;//因为后面也需要用
	}
}
int main()
{
	solve();
}

D

题意:给定数组长度,再给定m个关系,每个关系保证给定x, y, v,使得a[x] | a[y] = v。求该数组的最小字典序。

核心思路:首先我们可以知道这个方程必然是有解的,然后考虑一些或操作的性质。就是如果v上的二进制某个位置是0,那么a[x]与a[y]也是。所以我们整个题目需要按位考虑

然后我们注意a[x]可能或不同的a[y]等于不同的v,所以这个题目我们可以考虑建立一个图,点是a[x],a[y].边权是x。

然后我们注意题目要求最小字典序,所以我们先把每个a[i]的每一个二进制位置都放1.然后根据a[j]和v的情况放0。我们从1-n进行一个贪心,就是高位到低位进行贪心的放0。、

下面这是一个简单的图:

#include<bits/stdc++.h>
#include<unordered_map>
#define IOS std::ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
using namespace std;
typedef long long LL;
const int N = 2e5+10;
LL inf = (1LL << 30)-1 ;
typedef pair<int, int> PII;
int a[N],vis[N];
int n, q;
void slove() {
    cin >> n >> q;
    vector<vector<PII>> g(N);   
    for (int i = 1; i <= n; i++)a[i] = (1 << 30) - 1;
    while (q--) {
        int i, j, x; cin >> i >> j >> x;
        if (i == j) {
            vis[i] = 1;
            a[i] = x;
        }
        else {
            g[i].push_back({ j,x });
            g[j].push_back({ i,x });
            for (int k = 0; k <= 29; k++)if ((x >> k & 1) == 0) {
                if (a[i] >> k & 1)a[i] ^= (1 << k);
                if (a[j] >> k & 1)a[j] ^= (1 << k);
            }
        }
    }
    for (int k = 29; k >= 0; k--) {
        for (int i = 1; i <= n; i++) {
            if (vis[i])continue;
            if ((a[i] >> k & 1) == 0)continue;
            //a[i]  在第k位上是1 ,我们看看他能不能变成0
            bool flag = 1;
            for (auto p : g[i]) {
                int j = p.first, x = p.second;
                if ((x >> k & 1) == 0)continue;//只要 第k位上为 1的边
                if ((a[j] >> k & 1) == 0)flag = 0;
            }
            if (flag)a[i] ^= (1 << k);
        }
    }
    for (int i = 1; i <= n; i++)cout << a[i] << " ";
    cout << endl;
}
int main()
{
    slove();
}

这次的div2的c有点难,想了比较久。

posted @ 2022-12-02 18:03  努力的德华  阅读(38)  评论(0)    收藏  举报