Oct. Training 2
D - Triple Sword Strike
<>
题意
一个n * n的方阵中有n个怪兽,每个怪兽对应一个val值,一个技能可以打死一行或一列的所有怪兽,问最多用三次这个技能,打死怪兽的价值和最大是多少。
思路
对于每行每列 我们都先预处理出怪兽的价值和。
三次操作只有四种可能:三行、三列、一行两列、一列两行,前两种可以先特判一下,主要是后面两种情况,如果直接枚举行和列会超时,
可以注意到假如我们选定一行为被使用技能的,然后找该行上所有点出现过的列(没出现过就不用),然后对于这样的列 列值和都减去在这列在该行上的
点值 取处理后列值和最大的两列就是我们选择该行的情况下,选择列的最优解。
对于求价值和最大的两个列,我们可以用multiset处理,键值自动排序来降低复杂度,每次删除先用find找到位置再删就好了 然后操作完一行再复原以免影响后续操作。
按这样枚举每行 和每列 复杂度是两倍点的个数
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const int N = 1e6 + 5;
const int maxn = 1e5 + 10;
int n, visr[N], visc[N], valr[N], valc[N];
map<pair<int, int>, int>mp;
//rr: 每行有那些列 cc: 每列有哪些行, r: 行序号 c: 列序号
vector<int>rr[N], cc[N], r, c;
//st1: 行值和 st2: 列值和
multiset<int, greater<>>st1, st2;
void solve()
{
cin >> n;
int x, y, v;
for (int i = 1; i <= n; i++) {
cin >> x >> y >> v;
mp[{x, y}] = v;
if (!visr[x]) r.emplace_back(x);
if (!visc[y]) c.emplace_back(y);
valr[x] += v;
valc[y] += v;
visr[x] = visc[y] = 1;
rr[x].emplace_back(y);
cc[y].emplace_back(x);
}
for (auto x : r) st1.insert(valr[x]);
for (auto x : c) st2.insert(valc[x]);
int ans = 0, x1 = 0, x2 = 0;
auto t = st1.begin();
auto t2 = st2.begin();
for (int i = 1; i <= 3 && t != st1.end(); i++) {
x1 += *t;
t++;
}
for (int i = 1; i <= 3 && t2 != st2.end(); i++) {
x2 += *t2;
t2++;
}
ans = max({ ans, x1, x2 });
for (auto& i : r) {
for (auto& j : rr[i]) {
st2.erase(st2.find(valc[j]));
st2.insert(valc[j] - mp[{i, j}]);
}
int x1 = 0;
auto t = st2.begin();
for (int i = 1; i <= 2 && t != st2.end(); i++) {
x1 += *t;
t++;
}
ans = max(ans, valr[i] + x1);
for (auto& j : rr[i]) {
st2.erase(st2.find(valc[j] - mp[{i, j}]));
st2.insert(valc[j]);
}
}
for (auto& i : c) {
for (auto& j : cc[i]) {
st1.erase(st1.find(valr[j]));
st1.insert(valr[j] - mp[{j, i}]);
}
int x1 = 0;
auto t = st1.begin();
for (int i = 1; i <= 2 && t != st1.end(); i++) {
x1 += *t;
t++;
}
ans = max(ans, valc[i] + x1);
for (auto& j : cc[i]) {
st1.erase(st1.find(valr[j] - mp[{j, i}]));
st1.insert(valr[j]);
}
}
cout << ans << "\n";
}
signed main()
{
IOS;
int _t = 1;
//cin >> _t;
while (_t--)
solve();
return 0;
}
F - Stones 1
https://codeforces.com/gym/103855/problem/F
题意
有n个石头 每个石头都有一个颜色(黑或白)和一个价值,除了最两边的石子,如果一个石子的颜色两边的石子的颜色和它自己的颜色不一样就可以吧这个石子取掉,得到相应的价值。
问最大价值
思路
先把连续的一块颜色合并成一个价值设定为最大的那个,这就变成了WBWBWBW这样的形式,然后我们去掉头和尾的石子,剩余的石子取最大的一半(向上取整),我们一定能取到剩下的最大的一半石子
如果不在最大的里面就设定成wwb bbw这种情况价值不加,模拟一下不难发现。
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const int N = 1e6 + 5;
ll n, a[N], b[N];
string s;
void solve()
{
cin >> n;
cin >> s;
s = ' ' + s;
for (int i = 1; i <= n; i++) cin >> a[i];
ll tot = 0;
s[0] = s[1];
ll mx = s[1];
for (int i = 1; i <= n; i++) {
if (s[i] != s[i - 1]) {
b[++tot] = mx;
mx = a[i];
}
else mx = max(mx, a[i]);
}
b[++tot] = mx;
ll ans = 0;
vector<ll>ve;
for (int i = 2; i < tot; i++) ve.push_back(b[i]);
sort(ve.begin(), ve.end(), greater<>());
for (int i = 0; i <= ((int)ve.size() + 1) / 2 - 1; i++) ans += ve[i];
cout << ans << "\n";
}
signed main()
{
IOS;
int _t = 1;
//cin >> _t;
while (_t--)
solve();
return 0;
}
H - Beacon Towers
https://codeforces.com/gym/103855/problem/H
题意
给你一个n 和一个长度为n的数组 将这个数组分为几段(连续的),这几段的最大值必须递增,求有几种方案,取模1e9+7
思路
对于分最多的段,我们贪心地取,大肯定是从第一个数开始,往后依次找递增的数,这样就可以保证选的相邻每两个数之间的数都比这两个数小,
然后对于这些数可以向左或者向右归并。然后我们每相邻选定的两个数之间分开,有几种分法,即他们的下标差。
dp[i]代表就前i个选定的数有几种方案
那么dp方程就是:\(dp[i] = dp[i - 1] * dis[i]\)
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const int N = 5e5 + 5;
ll n, a[N], b[N], dis[N], mx[N], dp[N];
void solve()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
ll pre = 1, tot = 1;
b[1] = a[1];
for (int i = 1; i <= n; i++) {
if (a[i] > b[tot]) {
b[++tot] = a[i];
dis[tot] = i - pre + 1;
pre = i;
}
mx[i] = max(mx[i - 1], a[i]);
}
dp[1] = 1;
for (int i = 2; i <= n; i++) {
if (dis[i])
dp[i] = (dp[i - 1] * dis[i]) % mod;
else
dp[i] = dp[i - 1];
}
cout << dp[n] << "\n";
}
signed main()
{
IOS;
int _t = 1;
//cin >> _t;
while (_t--)
solve();
return 0;
}
M - Short Question
https://codeforces.com/gym/103855/problem/M
题意
给你n个点\((xi, yi)\) 求\(/sum_i = 1^n/sum_j = 1^nmin(|xi - xj|,|yi - yj|)\)
思路
经过公式转化很好得出 这就是求两两之间的曼哈顿距离减去切比雪夫距离
然后还需要知道两点:
1.原坐标系(x, y)的两个点之间的切比雪夫距离就是坐标变成\(((x + y)/ 2, (x - y)/2\)之间的曼哈顿距离
2.求多个点两两之间的曼哈短距离和,可以将x和y分开计算,将x从小到大排列,对一段相邻两个点的距离被加的次数就是左点以左的点数乘以右点以右的点数(包含左右点)。
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const int N = 1e6 + 5;
const int maxn = 1e5 + 10;
#define int ll
ll n, x[N], y[N], xx[N], yy[N];
void solve()
{
cin >> n;
int v, x1, x2;
vector<int>v1, v2, v3, v4;
for (int i = 1; i <= n; i++) {
cin >> x1;
x[i] = x1;
}
for (int i = 1; i <= n; i++) {
cin >> x2;
y[i] = x2;
xx[i] = x2 + x[i];
yy[i] = x2 - x[i];
}
sort(x + 1, x + 1 + n);
sort(xx + 1, xx + 1 + n);
sort(y + 1, y + 1 + n);
sort(yy + 1, yy + 1 + n);
for (int i = 2; i <= n; i++) v1.push_back(x[i] - x[i - 1]);
for (int i = 2; i <= n; i++) v2.push_back(y[i] - y[i - 1]);
for (int i = 2; i <= n; i++) v3.push_back(xx[i] - xx[i - 1]);
for (int i = 2; i <= n; i++) v4.push_back(yy[i] - yy[i - 1]);
ll ans = 0, ans2 = 0;
for (int i = 0; i < n - 1; i++)
ans += v1[i] * (i + 1) * (n - 1 - i);
for (int i = 0; i < n - 1; i++)
ans += v2[i] * (i + 1) * (n - 1 - i);
for (int i = 0; i < n - 1; i++)
ans2 += v3[i] * (i + 1) * (n - 1 - i);
for (int i = 0; i < n - 1; i++)
ans2 += v4[i] * (i + 1) * (n - 1 - i);
cout << ans * 2 - ans2 << "\n";
}
signed main()
{
IOS;
int _t = 1;
//cin >> _t;
while (_t--)
solve();
return 0;
}
A - Factory Balls
https://codeforces.com/gym/103855/problem/A
题意
有n个球,k种颜色,m个装备,一开始每个球的颜色是1。
每个装备可以覆盖若干个球(可不连续,不同的装备覆盖的球肯能重复)
每次可以进行三个操作中的一个:
1.将没有被装备覆盖的球涂上m种颜色中的一种
2.将一种已装上的装备拆下
3.将一种为装上的装备装上
问最少多少次操作才能将每个球都涂成指定的颜色
思路
题目给定的数据很小\(1<=n,m,k <= 10\),可以状压表示状态进行暴力,每次枚举所有可能的操作
用s表示颜色的状态,t表示装备的状态,而对于每次操作s和t都可能变,状态变化是任意的,所以无法直接线性dp
可以bfs来处理,每次被更新的状态就进入队列用于作为转移态
#include<bits/stdc++.h>
#include<unordered_map>
#include<array>
#define ll long long
#define int ll
#define ull unsigned long long
#define all(a) a.begin(),a.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
const ll mod = 1e9 + 7;
const int N = 1e5 + 5;
const int maxn = 1e5 + 10;
int n, m, k, a[15], b[15][15], sz[15];
int f[1<<12][1<<12], b2[15], vis[15];
void solve()
{
cin >> n >> k >> m;
for (int i = 0; i < n; i++) cin >> a[i], a[i]--;
for (int i = 0; i < m; i++) {
cin >> sz[i];
for (int j = 1, x; j <= sz[i]; j++) {
cin >> x;
//b存相应装备可覆盖的球
b[i][j] = x - 1;
}
}
memset(f, inf, sizeof f);
queue<pair<int, int>>q;
int s = 0;
int t = 0;
for (int i = 0; i < n; i++)
if (a[i] == 0) s += (1 << i);
q.push({ s, t });
f[s][t] = 0;
while (!q.empty()) {
auto [s, t] = q.front();
q.pop();
for (int i = 0; i < m; i++) {
if (f[s][t ^ (1 << i)] > f[s][t] + 1) {
f[s][t ^ (1 << i)] = f[s][t] + 1;
q.push({ s, t ^ (1 << i) });
}
}
//vis[i]代表第i个位置是否被覆盖
memset(vis, 0, sizeof vis);
for (int i = 0; i < m; i++)
if ((t >> i) & 1)
for (int j = 1; j <= sz[i]; j++)
vis[b[i][j]] = 1;
for (int i = 0; i < k; i++) {
int temp = s;
for (int j = 0; j < n; j++) {
if (!vis[j]) {
if (a[j] == i) temp |= (1 << j);
else if((temp>>j) & 1) temp ^= (1 << j);
}
}
if (f[temp][t] > f[s][t] + 1) {
f[temp][t] = f[s][t] + 1;
q.push({ temp, t });
}
}
}
if (f[(1 << n) - 1][0] >= inf) cout << -1 << '\n';
else cout << f[(1 << n) - 1][0] << '\n';
}
signed main()
{
IOS;
int _t = 1;
//cin >> _t;
while (_t--)
solve();
return 0;
}