盖世计划--0731--AB班模拟

今天的题不算难,但是没做出一题,有点失败。

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

显然除了关键的 logn 个点其他位置等价,可以组合数计算。假如最终函数停止时左边的关键点有 L 个,右边有 R 个,停止时是否找到 x 标记为 E。那么对一个三元组 (L,R,E),考虑一个 y<x 的贡献:

yL(x2L1)(nxR)(L1)!R!(nLRE)!

y>xy=x 类似。

可以发现三元组与 x 无关,考虑一次 dfs 预处理出所有三元组,每次询问枚举每个三元组求出每个的答案即可。

复杂度 O(nlogn+qlog2n)

#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(n2) 的。考虑优化建边。

我们不关心边是什么,我们只关心连通性,观察到值域只有 220,考虑在值域上建边。

每个操作可以看成连向他的补集,然后补集连向他所有的子集。这样建图在新图上无向边的连通块就变成了强连通分量,跑一遍 tarjan 输出答案即可。

复杂度 O(nlogn)

#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(n2logn) 的暴力,枚举两个点计算答案。但这是没有前途的。

考虑枚举颜色,把式子写出来。

cc[li,ri]c[lj,rj]Kkikj(di+dj2dlca(i,j))

K 提出,然后整理好。

cKc[li,ri]c[lj,rj](dikikj+djkikj)2dlca(i,j)kikj

考虑前面括号的部分的处理,实际上可以写成:

diki1kj+djkj1ki

如果设 s0=dikis1=1ki,那么 s0s1 基本为上式(只不过要减去 i=j 的部分),所以从小到大枚举 c 的时候扫描线,维护这两个值即可。

lca 的部分用上经典的 trick,将式子拆成:2dlca(i,j)ki1kj。前者直接作为 i 到根的路径贡献到每一条边。当插入 新的点 j 时,查询 j 到根的路径上的和再乘上 1kj 即为贡献,这部分可以树剖+线段树维护区间和。

复杂度 O(nlogn)

#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;
}
posted @   Fire_Raku  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示