[賽記] csp-s模擬5

Peppa_Even_Pig發表於2024-09-29

光 100pts

賽時打的錯解A了,就很神奇;

其實可以發現答案有可二分性,考慮二分答案,每次check時列舉左上角和右下角的耗電量,然後對左下角的耗電量再進行二分,最後判定以下即可;

賽時就這麼打的,然後賽後拍出來了;

其實這個思路是對的,只是 $ \lfloor \frac n4 \rfloor $ 這個條件有誤差,所以暴力在這個範圍內判斷一下即可;

時間複雜度:$ O(n^2 \log^2 n) $,常數極大,但是跑不滿,可以過;

爬 10pts

直接暴搜10pts;

考慮正解,我們發現,能在某一個節點產生貢獻的只有它自己和其直接兒子,所以只考慮這一部分即可;

因為是異或,考慮對值進行二進位制拆分,依次考慮每一位的貢獻;

假設現在考慮到第 $ k $ 位,設當前節點 + 其直接兒子為 $ tot $,其中這一位為 $ 1 $ 的數的個數有 $ sum $ 個;

先考慮當前節點不是根的情況:

那麼只有當有奇數個這一位為 $ 1 $ 的數在這個節點時,這一位才能產生貢獻,其它數咋跳都沒有影響,這個方案數為:

\[(C_{sum}^{1} + C_{sum}^{3} + C_{sum}^{5} + ...) \times 2^{n - sum - 1} \]

其中:

\[C_{sum}^{1} + C_{sum}^{3} + C_{sum}^{5} + ... = 2^{sum - 1} \]

注意上面兩項並不能合併(因為前一項是有值的,合併以後可能就沒值了);

然後減去只有一個數的情況,考慮 $ sum $ 個數,每個數只上去一次,剩下的 $ tot - 1 $ 個數不動,其餘的 $ n - tot - 1 $ 個數隨便,方案數為:

\[sum \times 2^{n - tot - 1} \]

總方案數為:

\[2^{sum - 1} \times 2^{n - sum - 1} - sum \times 2^{n - tot - 1} \]

最後乘一個 $ 2^k $ 即可;

對於是根的情況,分奇偶討論一下即可(因為根不能往上跳);

時間複雜度 $ \Theta(n \log 1000000000) $;

點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long mod = 1e9 + 7;
int n;
long long a[500005];
long long ans;
struct sss{
	int t, ne;
}e[200005];
int h[200005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
long long fac[100005], fav[100005], p[100005];
long long ksm(long long a, long long b) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
long long C(long long n, long long m) {
	if (n == m) return 1;
	if (m == 0) return 1;
	if (n < m) return 0;
	return fac[n] * fav[m] % mod * fav[n - m] % mod;
}
int main() {
	freopen("climb.in", "r", stdin);
	freopen("climb.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	int xx;
	for (int i = 2; i <= n; i++) {
		cin >> xx;
		add(xx, i);
	}
	fac[0] = 1;
	fav[0] = 1;
	p[0] = 1;
	for (int i = 1; i <= n; i++) {
		fac[i] = fac[i - 1] * i % mod;
		fav[i] = ksm(fac[i], mod - 2);
		p[i] = p[i - 1] * 2 % mod;
	}
	for (int k = 30; k >= 0; k--) {
		for (int i = 1; i <= n; i++) {
			int sum = 0;
			int tot = 1;
			for (int j = h[i]; j; j = e[j].ne) {
				int u = e[j].t;
				tot++;
				if ((1 << k) & a[u]) sum++;
			}
			if (tot == 1) continue;
			if ((1 << k) & a[i]) sum++;
			if (sum == 0) continue;
			if (i == 1) {
				bool vis = ((1 << k) & a[i]);
				if (vis) {
					long long o = 0;
					o = ((p[max(0, sum - 2)] * p[max(0, n - sum)] % mod - p[max(0, n - tot)]) % mod + mod) % mod;
					ans = (ans + (1 << k) * o % mod) % mod;
				} else {
					long long o = 0;
					o = (p[max(0, sum - 1)] * p[max(0, n - sum - 1)]) % mod;
					ans = (ans + (1 << k) * o % mod) % mod;
				}
			} else {
				long long o = 0;
				o = ((p[max(0, sum - 1)] * p[max(0, n - sum - 1)] % mod - sum * p[max(0, n - tot - 1)] % mod) % mod + mod) % mod;
				ans = (ans + (1 << k) * o % mod) % mod;
			}
		}
	}
	cout << ans;
	return 0;
}

字串 50pts;

賽時 $ \Theta(Tn^4) $ 暴力DP 50pts也是沒誰了;

正解是貪心;

因為對於 $ B $ 有 $ c $ 的限制,所以我們構造出一種形如以 $ c $ 個 $ B $ 和 $ 1 $ 個 $ A $ 為迴圈節,迴圈 $ i $ 次的字串;

如:當 $ c = 3 $ ,$ i = 2 $ 時,我們構造出的字串為 $ BBBABBBA $,特別的,當 $ i = 1 $ 時,字串是形如 $ BBBAAA $ ($ m $ 個 $ B $ 和 $ n $ 個 $ A $)的;

然後我們考慮列舉 $ i $,每次加入剩下的 $ A $ 和 $ B $;

先加 $ A $,從頭加一個可以獲得 $ 1 $ 的貢獻,然後每剩下 $ a $ 個 $ A $ 就有 $ 1 $ 的貢獻;

再加 $ B $,從末尾加一個可以獲得 $ 1 $ 的貢獻,然後先把迴圈節裡的 $ B $ 補充成 $ b + 1 $ 的倍數並依據給的公式計算其貢獻,然後每剩下 $ b $ 個 $ B $ 就有 $ 1 $ 的貢獻;

有點細節需要注意;

點選檢視程式碼
#include <iostream>
#include <cstdio>
using namespace std;
int t;
int n, m, a, b, c;
int ans, sum;
int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while(t--) {
		cin >> n >> m >> a >> b >> c;
		ans = 0;
		for (int i = 0; i <= min(n, m / c); i++) {
			sum = 0;
			if (i == 0) {
				sum++;
				sum += (n - 1) / a;
				sum += (m - 1) / b;
				sum++;
				ans = max(ans, sum);
				continue;
			}
			sum++;
			sum += (2 * i - 1);
			int suma = n - i;
			if (suma > 0) {
				sum++;
				suma--;
			}
			if (suma > 0) sum += (suma / a);
			int sumb = m - c * i;
			if (sumb > 0) {
				sumb--;
				sum++;
			}
			if (sumb > 0) {
				int o = c % b;
				int res = 0;
				if (o == 0) res = 1;
				else res = b + 1 - o;
				if (i * res >= sumb) {
					int ss = sumb / res;
					sum += ss * ((c + res - 1) / b);
					sum += (i - ss) * ((c - 1) / b);
				} else {
					sum += i * ((c + res - 1) / b);
					sumb -= res * i;
					sum += (sumb / b);
				}
			} else {
				sum += i * ((c - 1) / b);
			}
			ans = max(ans, sum);
		}
		cout << ans << '\n';
	}
	return 0;
}

奇怪的函式 30pts

賽時 $ \Theta(nq) $ 暴力能有30pts更是沒誰了;

To be continued...

相關文章