蓋世計劃--0731--AB班模擬

Fire_Raku發表於2024-08-02

今天的題不算難,但是沒做出一題,有點失敗。

A

你打完表之後發現並沒有什麼出色的性質。只能考慮爆搜。

程式碼好寫,但是你要分析複雜度

最關鍵的一點是每一次遞迴至少多一個 \(1\),而 \(1\) 可以直接 return,所以最多遞迴 \(m\) 次就夠了。


#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
i64 x, k, m, ans;
std::vector<i64> v;
void dfs(i64 now, i64 l) {
	if(!m) return;
	if(now == 1 || !l) {
		ans += now;
		m--;
		return;
	}
	for(i64 c : v) {
		if(c > now) break;
		if(!(now % c)) dfs(c, l - 1);
		if(!m) return;
	}
}
int main() {
	freopen("trans.in", "r", stdin);
	freopen("trans.out", "w", stdout);
	
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> x >> k >> m;

	if(k >= m) {
		std::cout << m << "\n";
		return 0;
	}
	for(i64 i = 1; i * i <= x; i++) {
		if(!(x % i)) {
			v.pb(i);
			if(x / i != i) v.pb(x / i);
		}
	}
	std::sort(v.begin(), v.end());

	k = std::min(k, m);
	dfs(x, k);

	std::cout << ans << "\n";

	return 0;
}

B

顯然除了關鍵的 \(\log n\) 個點其他位置等價,可以組合數計算。假如最終函式停止時左邊的關鍵點有 \(L\) 個,右邊有 \(R\) 個,停止時是否找到 \(x\) 標記為 \(E\)。那麼對一個三元組 \((L,R,E)\),考慮一個 \(y<x\) 的貢獻:

\[yL{x-2\choose L-1}{n-x\choose R}(L-1)!R!(n-L-R-E)! \]

\(y> x\)\(y=x\) 類似。

可以發現三元組與 \(x\) 無關,考慮一次 dfs 預處理出所有三元組,每次詢問列舉每個三元組求出每個的答案即可。

複雜度 \(O(n\log n+q\log^2n)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 3e5 + 10, mod = 1e9 + 7, inv2 = (mod + 1) / 2;
i64 n, q;
std::map<std::array<i64, 3>, i64> mp;
i64 fac[N], inv[N], ans;
void dfs(i64 l, i64 r, i64 ls, i64 rs) {
	if(l > r) {
		mp[{ls, rs, 0}]++;
		return;
	}
	mp[{ls, rs, 1}]++;
	int mid = (l + r) >> 1;
	dfs(l, mid - 1, ls, rs + 1), dfs(mid + 1, r, ls + 1, rs);
}
i64 qpow(i64 a, i64 b) {
	i64 ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ret;
}
void init() {
	fac[0] = 1;
	for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
	inv[n] = qpow(fac[n], mod - 2);
	for(int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
i64 C(i64 n, i64 m) {
	if(m > n) return 0;
	return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
i64 sum(i64 l, i64 r) {
	return (l + r) * (r - l + 1) % mod * inv2 % mod;
}
int main() {
	freopen("binary.in", "r", stdin);
	freopen("binary.out", "w", stdout);

    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> q;

	dfs(1, n, 0, 0);
	init();

	while(q--) {
		i64 x;
		std::cin >> x;

		ans = 0;
		for(auto c : mp) {
			i64 L = c.fi[0], R = c.fi[1], E = c.fi[2], cnt = c.se;
			if(L) ans = (ans + cnt % mod * sum(1, x - 1) % mod * L % mod * C(x - 2, L - 1) % mod * C(n - x, R) % mod * fac[L - 1] % mod * fac[R] % mod * fac[n - L - R - E] % mod) % mod;
			if(R) ans = (ans + cnt % mod * sum(x + 1, n) % mod * R % mod * C(n - x - 1, R - 1) % mod * C(x - 1, L) % mod * fac[R - 1] % mod * fac[L] % mod * fac[n - L - R - E] % mod) % mod;
			if(E) ans = (ans + cnt % mod * x % mod * C(x - 1, L) % mod * fac[L] % mod * C(n - x, R) % mod * fac[R] % mod * fac[n - L - R - E] % mod) % mod; 
		}

		std::cout << ans << "\n";
	}

	return 0;
}

C

顯然的做法,每個操作看成一條邊,那麼在一個連通塊內的點內部一定能夠排序成從小到大的形態,所有連通塊拼起來就是最小字典序。問題出在邊數是 \(O(n^2)\) 的。考慮最佳化建邊。

我們不關心邊是什麼,我們只關心連通性,觀察到值域只有 \(2^{20}\),考慮在值域上建邊。

每個操作可以看成連向他的補集,然後補集連向他所有的子集。這樣建圖在新圖上無向邊的連通塊就變成了強連通分量,跑一遍 tarjan 輸出答案即可。

複雜度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 2e6 + 10;
int n;
int a[N];
int dfn[N], low[N];
int ins[N];
int st[N];
int bel[N];
int cnt[N];
int vis[N];
int pos[N];
std::vector<int> e[N], ans[N];
std::vector<pii> g[N];
int top, idx, tot;
void tarjan(int u) {
	st[++top] = u, ins[u] = 1;
	dfn[u] = low[u] = ++tot;
	for(int v : e[u]) {
		if(!dfn[v]) {
			tarjan(v);
			low[u] = std::min(low[u], low[v]);
		} else if(ins[v]) {
			low[u] = std::min(low[u], dfn[v]);
		}
	}
	if(low[u] == dfn[u]) {
		++idx;
		int v;
		do {
			v = st[top--];
			if(cnt[v]) {
				g[idx].pb({v, cnt[v]});
			}
			bel[v] = idx;
			ins[v] = 0;
		} while(v != u);
	}
}
int main() {
	freopen("seq.in", "r", stdin);
	freopen("seq.out", "w", stdout);
	
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;

	int lim = (1 << 20) - 1;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i];
		cnt[a[i]]++;
		e[a[i]].pb(lim ^ a[i]);
	}

	for(int i = 1; i <= lim; i++) {
		for(int j = 0; j < 20; j++) {
			if((i >> j) & 1) {
				e[i].pb(i ^ (1 << j));
			}
		}
	}

	for(int i = 0; i <= lim; i++) {
		if(!dfn[i]) tarjan(i);
	}

	for(int i = 1; i <= idx; i++) {
		if(!g[i].size()) continue;
		std::sort(g[i].begin(), g[i].end());
		for(int j = 0; j < g[i].size(); j++) {
			for(int k = 1; k <= g[i][j].se; k++) ans[i].pb(g[i][j].fi);
		}
	}

	for(int i = 1; i <= n; i++) {
		std::cout << ans[bel[a[i]]][pos[bel[a[i]]]++] << " ";
	}
	std::cout << "\n";

	return 0;
}

D

簡單的是 \(O(n^2\log n)\) 的暴力,列舉兩個點計算答案。但這是沒有前途的。

考慮列舉顏色,把式子寫出來。

\[\sum\limits_{c}\sum\limits_{c\in[l_i,r_i]}\sum\limits_{c\in[l_j,r_j]}\frac{K}{k_ik_j}(d_i+d_j-2d_{lca(i,j)}) \]

\(K\) 提出,然後整理好。

\[\sum\limits_{c}K\sum\limits_{c\in[l_i,r_i]}\sum\limits_{c\in[l_j,r_j]}(\frac{d_i}{k_ik_j}+\frac{d_j}{k_ik_j})-\frac{2d_{lca(i,j)}}{k_ik_j} \]

考慮前面括號的部分的處理,實際上可以寫成:

\[\frac{d_i}{k_i}\cdot\frac{1}{k_j}+\frac{d_j}{k_j}\cdot\frac{1}{k_i} \]

如果設 \(s_0=\sum\frac{d_i}{k_i}\)\(s1=\sum\frac{1}{k_i}\),那麼 \(s_0\cdot s_1\) 基本為上式(只不過要減去 \(i=j\) 的部分),所以從小到大列舉 \(c\) 的時候掃描線,維護這兩個值即可。

\(lca\) 的部分用上經典的 trick,將式子拆成:\(\frac{2d_{lca(i,j)}}{k_i}\cdot\frac{1}{k_j}\)。前者直接作為 \(i\) 到根的路徑貢獻到每一條邊。當插入 新的點 \(j\) 時,查詢 \(j\) 到根的路徑上的和再乘上 \(\frac{1}{k_j}\) 即為貢獻,這部分可以樹剖+線段樹維護區間和。

複雜度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define pii std::pair<i64, i64>
#define mk std::make_pair
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
/*

*/
const int N = 1e5 + 10, mod = 1e9 + 7;
i64 ans, K = 1;
int n, idx, maxn;
int l[N], r[N];
int id[N];
int top[N];
int sz[N];
int fa[N];
int len[N];
i64 inv[N];
int son[N];
i64 dep[N];
std::vector<pii> v[N]; 
std::vector<int> e[N];
i64 qpow(i64 a, i64 b) {
	i64 ret = 1;
	while(b) {
		if(b & 1) ret = ret * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ret;
}
void dfs1(int u, int f) {
	dep[u] = dep[f] + 1;
	fa[u] = f, sz[u] = 1;
	for(auto v : e[u]) {
		if(v == f) continue;
		dfs1(v, u);
		sz[u] += sz[v];
		if(sz[son[u]] < sz[v]) son[u] = v;  
	}
}
void dfs2(int u, int topf) {
	id[u] = ++idx;
	top[u] = topf;
	if(!son[u]) return;
	dfs2(son[u], topf);
	for(auto v : e[u]) {
		if(v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}
struct seg {
	i64 v, tg;
} t[N << 2];
void pushup(int u) {t[u].v = (t[u << 1].v + t[u << 1 | 1].v + mod) % mod;}
void mdf(seg &u, i64 v, int l, int r) {
	u.v = (u.v + (r - l + 1) * v % mod) % mod;
	u.tg = (u.tg + v) % mod;
}
void pd(int u, int l, int r) {
	if(!t[u].tg) return;
	int mid = (l + r) >> 1;
	mdf(t[u << 1], t[u].tg, l, mid);
	mdf(t[u << 1 | 1], t[u].tg, mid + 1, r);
	t[u].tg = 0;
}
i64 ask(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		return t[u].v;
	}
	int mid = (l + r) >> 1; pd(u, l, r);
	i64 ret = 0;
	if(L <= mid) ret = (ret + ask(u << 1, l, mid, L, R) + mod) % mod;
	if(R > mid) ret = (ret + ask(u << 1 | 1, mid + 1, r, L, R) + mod) % mod;
	return ret; 
}
void add(int u, int l, int r, int L, int R, i64 x) {
	if(L <= l && r <= R) {
		mdf(t[u], x, l, r);
		return;
	}
	int mid = (l + r) >> 1; pd(u, l, r);
	if(L <= mid) add(u << 1, l, mid, L, R, x);
	if(R > mid) add(u << 1 | 1, mid + 1, r, L, R, x);
	pushup(u);
}
void upd(int u, int v) {
	while(u) {
		add(1, 1, n, id[top[u]], id[u], v);
		u = fa[top[u]];
	}
}
i64 qry(int u) {
	i64 ret = 0;
	while(u) {
		ret = (ret + ask(1, 1, n, id[top[u]], id[u]) + mod) % mod;
		u = fa[top[u]];
	}	
	return ret;
}
int main() {
	freopen("route.in", "r", stdin);
	freopen("route.out", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;

	for(int i = 1; i <= n; i++) {
		std::cin >> l[i] >> r[i];
		v[l[i]].pb({i, 1});
		v[r[i] + 1].pb({i, -1});
		len[i] = r[i] - l[i] + 1;
		inv[i] = qpow(len[i], mod - 2);
		K = K * len[i] % mod; 
		maxn = std::max(maxn, r[i]);
	}

	for(int i = 1; i < n; i++) {
		int u, v;
		std::cin >> u >> v;
		e[u].pb(v), e[v].pb(u);
	}

	dfs1(1, 0), dfs2(1, 1);

	i64 s0 = 0, s1 = 0, s2 = 0, s3 = 0;
	for(int i = 1; i <= maxn; i++) {
		for(auto x : v[i]) {
			i64 c = x.fi;
			if(x.se == -1) {
				s0 = (s0 - (dep[c] * inv[c] % mod + mod) % mod) % mod;
				s1 = (s1 - inv[c] + mod) % mod;
				s2 = (s2 - (dep[c] * inv[c] % mod * inv[c] % mod + mod) % mod) % mod;
				upd(c, mod - inv[c]);
				s3 = (s3 - inv[c] * qry(c) % mod + mod) % mod;
			} else {
				s0 = (s0 + dep[c] * inv[c] % mod + mod) % mod;
				s1 = (s1 + inv[c] % mod) % mod;
				s2 = (s2 + dep[c] % mod * inv[c] % mod * inv[c] % mod) % mod;
				s3 = (s3 + inv[c] * qry(c) % mod) % mod;
				upd(c, inv[c]);
			}
		}
		ans = (ans + K * (((s0 * s1 % mod) - s2 + mod) % mod - 2ll * s3 + 2ll * mod) % mod) % mod;
	}

	std::cout << ans << "\n";

	return 0;
}

相關文章