[比赛链接](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
题意:就是一个公式,不太好解释。
核心思路
-
首先我们需要把整个数组对应的值求出来,这个怎么求呢。就是我们可以从前往后地推。假设我们插入的a[i]!=a[i-1],那就说明所有包含a[i]的区间,他们的一个贡献值也就是区间的价值都会加一,而我们的区间数目就是区间中点的数目,这个自己模拟下就懂了,所以总体价值加i.但是如果相等,那么就只能加1,就是加上a[i]自己的长度。
-
接下来就只考虑把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有点难,想了比较久。
标签:std,int,LL,Codeforces,long,Div,include,我们,816 From: https://www.cnblogs.com/xyh-hnust666/p/16945258.html