【CodeForces】CodeForces Round #511 (Div. 1) 题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82837083

【比赛链接】

【题解链接】

**【A】**Enlarge GCD

【思路要点】

  • 令所有数的 g c d gcd g g ,将所有数除去 g g ,问题变为了所有数的 g c d gcd 1 1 的情况。
  • 我们要选出一个最大的数集,使得这个数集中的数存在不为 1 1 的公因数。
  • 显然我们只需要考虑所有质数即可,线性筛求出每个数的最小质因子,质因数分解每一个数即可。
  • 时间复杂度 O ( N L o g V + V ) O(NLogV+V)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
const int MAXV = 1.5e7 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, tot, a[MAXN], prime[MAXV], f[MAXV], cnt[MAXV];
void init(int n) {
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = i;
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
		}
	}
}
int gcd(int x, int y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}
int main() {
	init(1.5e7);
	int g = 0; read(n);
	for (int i = 1; i <= n; i++) {
		read(a[i]);
		g = gcd(a[i], g);
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		int tmp = a[i] / g;
		while (tmp != 1) {
			cnt[f[tmp]]++;
			if (cnt[f[tmp]] > ans) ans = cnt[f[tmp]];
			int tnp = f[tmp];
			while (tmp % tnp == 0) tmp /= tnp;
		}
	}
	if (ans == 0) printf("-1\n");
	else printf("%d\n", n - ans);
	return 0;
}

**【B】**Little C Loves 3 II

【思路要点】

  • 打表题。
  • 不妨令 N M N≤M
  • 用最大流求出 N , M N,M 较小的时候的答案,我们发现当 N , M N,M 足够大时,答案为 2 N M 2 2*\lfloor\frac{N*M}{2}\rfloor ,特殊处理 N = 1 N=1 的情况即可。
  • 或者你也可以直接交一张表。
  • 时间复杂度 O ( 1 ) O(1)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int main() {
	ll n, m; read(n), read(m);
	if (n > m) swap(n, m);
	if (n == 1) {
		ll ans = m / 6 * 6;
		ll tmp = m % 6;
		ans += max(0ll, tmp - 3) * 2;
		writeln(ans);
	} else if (n == 2) {
		ll ans = n * m;
		if (m == 2) ans -= 4;
		if (m == 3) ans -= 2;
		if (m == 7) ans -= 2;
		writeln(ans);
	} else writeln(n * m - n * m % 2);
	return 0;
}

**【C】**Region Separation

【思路要点】

  • 1 1 号节点为根,节点 i i 的子树权值和为 s u m i sum_i
  • 若给定 k k ,一棵树能够被分成权值和相同的 k k 部分的充要条件为 s u m 1 k \frac{sum_1}{k} 为整数,并且存在 k k 个子树 i i 满足 s u m i sum_i s u m 1 k \frac{sum_1}{k} 的整数倍。
  • 因此,我们可以先计算出对于每一个 k k ,满足 s u m i sum_i s u m 1 k \frac{sum_1}{k} 的整数倍的子树 i i 的个数 c n t k cnt_k ,然后,在合法的 k k 上简单 d p dp ,得到答案。
  • 考虑一个子树 i i c n t k cnt_k 的贡献,由于 s u m i = t s u m 1 k   ( t Z ) sum_i=\frac{t*sum_1}{k}\ (t\in \Z) ,因此 k = t s u m 1 s u m i   ( t Z ) k=\frac{t*sum_1}{sum_{i}}\ (t\in \Z) ,即 k k t m p = s u m 1 g c d ( s u m 1 , s u m i ) tmp=\frac{sum_1}{gcd(sum_1,sum_i)} 的倍数,令 f t m p f_{tmp} 1 1 ,那么 c n t k = j / k f j cnt_k=\sum_{j/k}f_j ,简单计算 c n t k cnt_k 即可。
  • 时间复杂度 O ( N L o g N + N L o g V ) O(NLogN+NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int P = 1e9 + 7;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
ll gcd(ll x, ll y) {
	if (y == 0) return x;
	else return gcd(y, x % y);
}
int n, p[MAXN], cnt[MAXN], dp[MAXN];
ll a[MAXN];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	for (int i = 2; i <= n; i++)
		read(p[i]);
	for (int i = n; i >= 2; i--)
		a[p[i]] += a[i];
	for (int i = 1; i <= n; i++) {
		ll tmp = a[1] / gcd(a[i], a[1]);
		if (tmp <= n) cnt[tmp]++;
	}
	for (int i = n; i >= 1; i--)
	for (int j = 2 * i; j <= n; j += i)
		cnt[j] += cnt[i];
	int ans = 0; dp[1] = 1;
	for (int i = 1; i <= n; i++) {
		if (cnt[i] != i) continue;
		update(ans, dp[i]);
		for (int j = 2 * i; j <= n; j += i)
			if (cnt[j] == j) update(dp[j], dp[i]);
	}
	writeln(ans);
	return 0;
}

**【D】**Intervals of Intervals

【思路要点】

  • 考虑如何计算 [ l , r ] [l,r] 的权值,从左向右考虑每一个右端点 r r ,将 [ a r , b r ] [a_r,b_r] 赋值为 r r ,那么 [ l , r ] [l,r] 的权值即为 l ≥l 的位置的个数。
  • 对于一个右端点 r r ,我们希望维护所有左端点 l l 对应的 [ l , r ] [l,r] 的权值。在将 [ a r , b r ] [a_r,b_r] 赋值为 r r 时,若有 x x 个位置被由 f o r m e r former 赋值为了 r r ,那么 ( f o r m e r , r ] (former,r] 中的 l l 对应的 [ l , r ] [l,r] 的权值都会增加 x x
  • s e t set 存储染色情况,记录每一个 r r 处答案的增量。
  • 二分答案 m i d mid ,用 T w o P o i n t e r s TwoPointers 结合前文计算出的答案的增量计算权值大于等于 m i d mid 的方案数,得到权值第 k k 大的方案 v a l k valk
  • 再计算一下权值大于等于 v a l k + 1 valk+1 的方案和即可。
  • 时间复杂度 O ( N L o g N + N L o g V ) O(NLogN+NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 5;
const int INF = 1e9;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct info {int l, r, val; };
bool operator < (info a, info b) {return a.l < b.l; }
multiset <info> st;
int n, k, l[MAXN], r[MAXN];
vector <pair <int, int> > a[MAXN];
int cnt(int mid) {
	static int add[MAXN];
	memset(add, 0, sizeof(add));
	int ans = 0, l = 1, now = 0;
	for (int i = 1; i <= n; i++) {
		unsigned j = 0;
		while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second;
		while (l <= i && now >= mid) {
			now += add[++l];
			while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second;
		}
		ans += l - 1;
		while (j < a[i].size()) add[a[i][j].first] += a[i][j].second, j++;
		if (ans > INF) return INF;
	}
	return ans;
}
ll sum(int mid) {
	static int add[MAXN];
	memset(add, 0, sizeof(add));
	ll ans = 0; int l = 1, now = 0;
	for (int i = 1; i <= n; i++) {
		unsigned j = 0;
		while (j < a[i].size() && a[i][j].first <= l) {
			ans += (n - i + 1ll) * (l - a[i][j].first + 0ll) * a[i][j].second;
			now += a[i][j++].second;
		}
		while (l <= i && now >= mid) {
			ans += now * (n - i + 1ll);
			now += add[++l];
			while (j < a[i].size() && a[i][j].first <= l) now += a[i][j++].second;
		}
		while (j < a[i].size()) add[a[i][j].first] += a[i][j].second, j++;
	}
	return ans;
}
int main() {
	read(n), read(k);
	for (int i = 1; i <= n; i++)
		read(l[i]), read(r[i]), r[i]--;
	st.insert((info) {1, INF, 0});
	for (int i = 1; i <= n; i++) {
		int ql = l[i], qr = r[i], lv = i, rv = i;
		info now = (info) {l[i], r[i], i};
		multiset <info> :: iterator tmp = st.upper_bound(now);
		if (--tmp != st.end()) {
			ql = (*tmp).l;
			lv = (*tmp).val;
		}
		while (tmp != st.end() && (*tmp).l <= r[i]) {
			if ((*tmp).r >= r[i]) {
				qr = (*tmp).r;
				rv = (*tmp).val;
			}
			multiset <info> :: iterator tnp = tmp;
			a[i].push_back(make_pair((*tmp).val + 1, (*tmp).r - (*tmp).l + 1));
			tmp++; st.erase(tnp);
		}
		if (ql != l[i]) {
			st.insert((info) {ql, l[i] - 1, lv});
			a[i].push_back(make_pair(lv + 1, -(l[i] - ql)));
		}
		if (qr != r[i]) {
			st.insert((info) {r[i] + 1, qr, rv});
			a[i].push_back(make_pair(rv + 1, -(qr - r[i])));
		}
		st.insert((info) {l[i], r[i], i});
		a[i].push_back(make_pair(i + 1, -(r[i] - l[i] + 1)));
		sort(a[i].begin(), a[i].end());
	}
	int l = 1, r = 1e9;
	while (l < r) {
		int mid = (l + r + 1) / 2;
		if (cnt(mid) >= k) l = mid;
		else r = mid - 1;
	}
	ll ans = 1ll * l * (k - cnt(l + 1));
	writeln(ans + sum(l + 1));
	return 0;
}

**【E】**Little C Loves 3 III

【思路要点】

  • f ( i ) f(i) 表示 i i 二进制表示中 1 1 的个数。
  • A i = a i 4 f ( i ) A_i=a_i*4^{f(i)} B i = b i 4 f ( i ) B_i=b_i*4^{f(i)}
  • 计算 C i = j k = i A j B k C_i=\sum_{j|k=i}A_j*B_k ,那么 c i = C i f ( i ) %   4 c_i=\frac{C_i}{f(i)}\%\ 4
  • 时间复杂度 O ( 2 N N ) O(2^N*N)
  • 可以看一下题解中出题人是如何得到这个做法的,很有启发意义。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = (1 << 21) + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, Log, bits[MAXN];
char s[MAXN], t[MAXN];
ll a[MAXN], b[MAXN], c[MAXN], P;
void FWT(ll *a) {
	for (int bit = 1; bit <= Log; bit++)
	for (int i = 0; i < n; i += 1 << bit) {
		for (int j = i, k = i + (1 << (bit - 1)); k < i + (1 << bit); j++, k++) {
			a[k] += a[j];
			if (a[k] >= P) a[k] -= P;
		}
	}
}
void UFWT(ll *a) {
	for (int bit = 1; bit <= Log; bit++)
	for (int i = 0; i < n; i += 1 << bit) {
		for (int j = i, k = i + (1 << (bit - 1)); k < i + (1 << bit); j++, k++) {
			a[k] -= a[j];
			if (a[k] < 0) a[k] += P;
		}
	}
}
int main() {
	read(Log), n = 1 << Log;
	P = 1ll << (2 * Log + 2);
	scanf("\n%s", s);
	scanf("\n%s", t);
	for (int i = 0; i < n; i++) {
		if (i != 0) bits[i] = bits[i - (i & -i)] + 1;
		a[i] = (s[i] - '0' + 0ll) << (bits[i] * 2);
		b[i] = (t[i] - '0' + 0ll) << (bits[i] * 2);
	}
	FWT(a), FWT(b);
	for (int i = 0; i < n; i++) {
		ll tmp = (ld) a[i] * b[i] / P;
		c[i] = ((a[i] * b[i] - tmp * P) % P + P) % P;
	}
	UFWT(c);
	for (int i = 0; i < n; i++) {
		c[i] >>= (bits[i] * 2);
		putchar((c[i] & 3) + '0');
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/82837083