Codeforces Round 952 (Div. 4)
A. Creating Words
void solve() {
string a, b;
cin >> a >> b;
swap(a[0], b[0]);
cout << a << ' ' << b << '\n';
}
B. Maximum Multiple Sum
总结:作为一个等差数列,快速的找到最高次系数,以及快速求和..C = n / x, sum = (c * x + x) * c / 2;
void solve() {
int n;
cin >> n;
int maxn = 0;
int ans = 0;
for (int x = 2; x <= n; ++x){
int t = n / x;
int sum = (t * x + x) * t / 2;
if (checkMax(maxn, sum)){
ans = x;
}
}
cout << ans << '\n';
}
C. Good Prefixes
题意:数组中一个数是其他所有数的和,则数组为good。求给定数组中前缀good数组的数量。
思路:每次新增一个数,分情况讨论。该数=prefix,该数<prefix, 该数>prefix。
总结:思维题吧,一开始没想到分情况讨论,卡了一小会。
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (auto& x : a){
cin >> x;
}
int last = -1;
long long sum = 0;
int ans = 0;
int maxn = 0;
for (int i = 0; i < n; ++i){
if (a[i] == sum || (!a[i] && i - 1 == last)){
ans ++;
last = i;
}
else if (a[i] < sum && sum + a[i] == 1ll * 2 * maxn){
ans ++;
last = i;
}
sum += a[i];
maxn = max(maxn, a[i]);
}
cout << ans << '\n';
}
D. Manhattan Circle
题意:矩阵中有一个左右对阵的形状,找到形状的中心坐标。
思路:一次遍历,找到上下左右四个端点,取中心即可。
void solve() {
int n, m;
cin >> n >> m;
vector<string> mat(n);
constexpr int inf = 0x3f3f3f3f;
int up = inf, left = inf, down = -inf, right = -inf;
for (int i = 0; i < n; ++i){
cin >> mat[i];
int p = mat[i].find_first_of('#');
int q = mat[i].find_last_of('#');
if (p != -1){
up = min(up, i + 1);
left = min(left, p + 1);
right = max(right, q + 1);
down = max(down, i + 1);
}
}
cout << (up + down) / 2 << ' ' << (left + right) / 2 << '\n';
}
E. Secret Box
题意:给定一个长方体xyz,代表3个维度长度,再给定一个体积k,问任何可能的形状(体积为k),在长方体内部可以放置的方式最多有多少个。
思路:x, y, z <= 2000。 暴力选择前两条边,然后可以得出每个维度可以移动的距离,相乘就是最大的放置方式。
总结:看到三维有点没理解,仔细分析后发现最后的答案就是在每个维度上能移动的距离,只要暴力选择边就好。选择边复杂度是(O(1e4))。可以加一些剪枝的优化操作进去。
void solve() {
array<int, 3> a;
for (int i = 0; i < 3; ++i){
cin >> a[i];
}
sort(a.begin(), a.end());
long long k;
cin >> k;
long long ans = 0;
for (int i = 1; i <= a[0]; ++i){
for (int j = 1; j <= a[1]; ++j){
long long cur = 1ll * i * j;
if (cur > k){
break;
}
if (k % cur || k / cur > a[2]){
continue;
}
int p = k / (1ll * i * j);
ans = max(ans, (1ll * (a[0] - i + 1) * (a[1] - j + 1) * (a[2] - p + 1)));
}
}
cout << ans << '\n';
}
F. Final Boss
题意:给定2个长度为n的数组a和c,和一个h,a代表能量,c代表使用该能量后冷却时间。每轮操作可以使用所有未在冷却时间中的能量攻击h,问最少多少轮可以让h <= 0。如果所有的能量都在冷却,则该轮只能干等。
思路:如果能量总和>=h,那么1次就可以i,否则二分,每个二分点上可以精确的计算出可以攻击的次数。
总结:一开始没读懂题,以为一轮操作只能释放一个能量,于是写了个小顶堆,但是测试样例不过才发现一次可以使用所有的能量。。
求攻击次数时要向上取余。
void solve() {
int n, h;
cin >> h >> n;
vector<int> a(n), c(n);
for (auto& x : a){
cin >> x;
}
for (int i = 0; i < n; ++i){
cin >> c[i];
}
if (accumulate(a.begin(), a.end(), 0ll) >= h){
cout << 1 << '\n';
return;
}
long long l = 1, r = (long long)1e18;
while (l < r){
long long mid = (l + r) >> 1;
long long sum = 0;
for (int i = 0; i < n; ++i){
sum += 1ll * ((mid + c[i] - 1) / c[i]) * a[i];
if (sum >= h){
break;
}
}
if (sum >= h){
r = mid;
}
else{
l = mid +1;
}
}
cout << l << '\n';
}
G. D-Function
题意:D(n)代表了n的数位和,求给定的区间内,k * D(n) = D(k * n)的数量。
思路:观察样例后发现,满足条件的数每个数位上的数 * K都不会进位,所以每个数位上的数的选择有q = 9 / k + 1中(不包括最高位),从[l, r)每个区间有(q - 1) * pow(q, l) + (q - 1) * pow(q, l + 1) + .. + (q - 1) * pow(q, r - 1) => (q - 1) *(pow(q, l) + pow(q, l + 1) + .. + pow(q, r - 1)) = (q - 1) * pow(q, l) * (pow(q, r - l) - 1) / (q - 1) = pow(q, l) * (pow(q, r - l) - 1)
void solve() {
long long l, r, k;
cin >> l >> r >> k;
if (k >= 10){
cout << 0 << '\n';
return;
}
MInt q = 9 / k + 1;
cout << fastPower(q, l) * (fastPower(q, r - l) - 1) << '\n';
}
后面还剩不少时间,有点困了,准备补剩下的题
H1. Maximize the Largest Component (Easy Version)
题意:给定一个n * m矩阵包含'.'和'#',上下左右相邻的#视为连通。问可以进行一次操作,选择某一行或者某一列,将其全变为#,问最大连通区域是多大。
思路:dsu合并连通块,对每行每列分别考虑,如果行(列)中的元素由.变为#,那么其相邻的行(列)如果是#,要将其所在的连通块考虑进来。
总结:一开始纯暴力,每次查询都copy一个dsu进来,将相邻的融合,其实不需要copy dsu,只要将相邻元素的连通块的头节点编号放到集合中,最后对集合中所有的元素做一次连通块大小查询即可。
/*
* DisjointSet(并查集)
* 设计思想:保留了基本的合并查询功能,采用了宏定义的方式,可手动指定增加新功能。
* 基于面向对象的编程思想,本方法尽可能多的隐藏了内部实现的细节,并且将必要的编程接口暴露在外部,并需要对这些接口进行直接的修改。
* 在该设计中,需要修改的接口有:
* USE_DSU_SET_ELEMENT:是否获取集合中的元素。
* 如果要保留并查集中每个集合中的具体元素,将该定义设置为true。
* USE_DSU_WEIGHT:是否使用带权并查集。
* 如果使用带权并查集,将该定义设置为true。
* 并且实现两个友元函数mergeWeights和compressWeights。
* unionWeights:在两个集合合并时调用,需要手动实现初始化权重的细节。
* compressWeights():在路径压缩时调用,需要手动实现权重更新的细节。
* 并查集的主体所有必备的方法都已实现,无需修改。
*
* gitHub(仓库地址): https://github.com/100000000000000000000000000000000/programming-template.git
*/
#define USE_DSU_SET_ELEMENT 0
#define USE_DSU_WEIGHT 1
class DisjointSet {
friend void unionWeights(DisjointSet& dsu, int x, int y, int px, int py, long long value);
friend void compressWeights(DisjointSet& dsu, int x, int y);
public:
DisjointSet(int sz) :
sz_(sz),
num_sets_(sz)
{
fa_.resize(sz_);
std::iota(fa_.begin(), fa_.end(), 0);
set_size_.assign(sz_, 1);
#if USE_DSU_WEIGHT
weight_.resize(sz_);
#endif
#if USE_DSU_SET_ELEMENT
elements_.resize(sz_);
for (int i = 0; i < sz_; ++i) {
elements_[i].emplace_back(i);
}
#endif
}
inline int findSet(int x) {
if (fa_[x] == x){
return x;
}
int par = fa_[x];
fa_[x] = findSet(fa_[x]);
#if USE_DSU_WEIGHT
compressWeights(*this, x, par);
#endif
return fa_[x];
}
inline int getSetSize(int x) {
return set_size_[findSet(x)];
}
#if USE_DSU_WEIGHT
long long getWeight(int x) {
findSet(x);
return weight_[x];
}
#endif
inline int countSets() {
return num_sets_;
}
inline bool isSameSet(int x, int y) {
return findSet(x) == findSet(y);
}
bool unionSet(int x, int y, long long value = 0) {
int px = findSet(x);
int py = findSet(y);
if (px == py) {
return false;
}
fa_[px] = py;
num_sets_--;
#if USE_DSU_WEIGHT
unionWeights(*this, x, y, px, py, value);
#endif
set_size_[py] += set_size_[px];
#if USE_DSU_SET_ELEMENT
elements_[y].insert(elements_[y].end(), elements_[x].begin(), elements_[x].end());
elements_[x].clear();
#endif
return true;
}
#if USE_DSU_SET_ELEMENT
inline std::vector<int> getSetElements(int x) {
return elements_[findSet(x)];
}
#endif
private:
int sz_;
int num_sets_;
std::vector<int> fa_;
std::vector<int> set_size_;
std::vector<long long> weight_;
};
/*
这里x和y是操作时的集合节点,要把x所在集合合并到y。px和py是前两者的集合代表元素。
value是一个缺省值,代表指定x->y的权值,默认为0。
*/
void unionWeights(DisjointSet& dsu, int x, int y, int px, int py, long long value = 0){
}
/*
这里是路径压缩时的更新权重操作,y是x压缩前的直接父亲节点。
*/
void compressWeights(DisjointSet& dsu, int x, int y){
}
template<typename T, typename U, typename V>
constexpr inline bool inRange(const T& a, const U& b, const V& c){
return static_cast<bool>(a >= b && a <= c);
}
using namespace std;
void preProcess() {
}
void solve() {
int n, m;
cin >> n >> m;
vector<vector<char>> mat(n, vector<char>(m));
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
cin >> mat[i][j];
}
}
int s = n * m + 1;
DisjointSet dsu(s);
auto number = [&](int i, int j){
return i * m + j;
};
for (int i = 0; i < n; ++i){
for (int j = 0; j < m; ++j){
if (mat[i][j] == '#'){
if (i > 0 && mat[i - 1][j] == '#'){
dsu.unionSet(number(i, j), number(i - 1, j));
}
if (j > 0 && mat[i][j - 1] == '#'){
dsu.unionSet(number(i, j), number(i, j - 1));
}
}
}
}
int ans = 0;
for (int i = 0; i < n; ++i){
set<int> sett;
for (int j = 0; j < m; ++j){
sett.insert(dsu.findSet(number(i, j)));
if (mat[i][j] == '.'){
if (i > 0 && mat[i - 1][j] == '#'){
sett.insert(dsu.findSet(number(i - 1, j)));
}
if (i < n - 1 && mat[i + 1][j] == '#'){
sett.insert(dsu.findSet(number(i + 1, j)));
}
}
}
int cnt = 0;
for (const auto& cur : sett){
cnt += dsu.getSetSize(cur);
}
ans = max(ans, cnt);
}
for (int j = 0; j < m; ++j){
set<int> sett;
int cnt = 0;
for (int i = 0; i < n; ++i){
sett.insert(dsu.findSet(number(i, j)));
if (j > 0 && mat[i][j - 1] == '#'){
sett.insert(dsu.findSet(number(i, j - 1)));
}
if (j < m - 1 && mat[i][j + 1] == '#'){
sett.insert(dsu.findSet(number(i, j + 1)));
}
}
for (const auto& cur : sett){
cnt += dsu.getSetSize(cur);
}
ans = max(ans, cnt);
}
cout << ans << '\n';
}
H2. Maximize the Largest Component (Hard Version)