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)

posted @ 2024-06-12 09:47  _Yxc  阅读(115)  评论(0编辑  收藏  举报