[赛记] 暑假集训CSP提高模拟 25

可持久化线段树 0pts

确实是板子题,可是我没打过标记永久化,结果干了3h最终爆零(还发明了一个不对的算法);

其实标记永久化挺好想的,可是赛时没想出来;

用个主席树上的标记永久化,查询时一路累加标记,记得修改时改掉原树的sum值;

当然也可以用可撤销线段树做,(就很简单,但是赛时没想)

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
#define int long long
const long long mod = 998244353;
int n, m;
int rt[500005];
int tot, cnt;
long long a[500005];
namespace top_tr{
	struct sss{
		int ls, rs;
		long long sum, lz;
	}tr[5000005];
	void bt(int &id, int l, int r) {
		if (!id) id = ++tot;
		if (l == r) {
			tr[id].sum += a[l];
			return;
		}
		int mid = (l + r) >> 1;
		bt(tr[id].ls, l, mid);
		bt(tr[id].rs, mid + 1, r);
		tr[id].sum = tr[tr[id].ls].sum + tr[tr[id].rs].sum;
	}
	void add(int &id, int l, int r, int L, int R, int d) {
		tr[++tot] = tr[id];
		id = tot;
		tr[id].sum += d * (min(r, R) - max(l, L) + 1);
		if (l >= L && r <= R) {
			tr[id].lz += d;
			return;
		}
		int mid = (l + r) >> 1;
		if (L <= mid) add(tr[id].ls, l, mid, L, R, d);
		if (R > mid) add(tr[id].rs, mid + 1, r, L, R, d);
	}
	long long ask(int id, int l, int r, int L, int R, long long tg) {
		if (l >= L && r <= R) return tr[id].sum + tg * (r - l + 1);
		int mid = (l + r) >> 1;
		if (R <= mid) return ask(tr[id].ls, l, mid, L, R, tg + tr[id].lz);
		else if (L > mid) return ask(tr[id].rs, mid + 1, r, L, R, tg + tr[id].lz);
		else return ask(tr[id].ls, l, mid, L, mid, tg + tr[id].lz) + ask(tr[id].rs, mid + 1, r, mid + 1, R, tg + tr[id].lz);
	}
}
using namespace top_tr;
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	bt(rt[0], 1, n);
	int s, l, r, x;
	for (int i = 1; i <= m; i++) {
		cin >> s;
		if (s == 1) {
			cin >> l >> r >> x;
			cnt++;
			rt[cnt] = rt[cnt - 1];
			add(rt[cnt], 1, n, l, r, x);
		}
		if (s == 2) {
			cin >> l >> r;
			cout << ask(rt[cnt], 1, n, l, r, 0) % mod << '\n';
		}
		if (s == 3) {
			cin >> x;
			cnt -= x;
		}
	}
	return 0;
}

Little Busters ! 25pts

赛时特殊性质25pts;

考虑一个环,其实就是一个边双强连通分量;

所以我们先只考虑Lun边,进行Tarjan缩点,然后把不在连通分量的边删除,最后对不连通的几个连通分量之间连Qie边即可,这里连通性用并查集维护即可;

挺好打的,我打一遍就过了

点击查看代码
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
using namespace std;
int n, m;
struct sss{
	int f, t, ne, w;
}e[500005];
int h[500005], cnt;
void add(int u, int v, int ww) {
	e[++cnt].t = v;
	e[cnt].f = u;
	e[cnt].ne = h[u];
	h[u] = cnt;
	e[cnt].w = ww;
}
int dfn[500005], low[500005];
bool vis[500005], bri[500005], vi[500005];
int belog[500005], ecc;
int dcnt;
vector<int> v[500005];
int fa[500005];
int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void Tarjan(int x, int fa) {
	dfn[x] = low[x] = ++dcnt;
	for (int i = h[x]; i; i = e[i].ne) {
		if (e[i].w == 2) continue;
		int u = e[i].t;
		if (u == fa) continue;
		if (!dfn[u]) {
			Tarjan(u, x);
			low[x] = min(low[x], low[u]);
			if (low[u] > dfn[x]) {
				if (i & 1) {
					bri[i] = bri[i + 1] = true;
				} else {
					bri[i] = bri[i - 1] = true;
				}
			}
		} else {
			low[x] = min(low[x], dfn[u]);
		}
	}
}
void dfs(int x) {
	belog[x] = ecc;
	vis[x] = true;
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (e[i].w == 2 || vis[u] || bri[i]) continue;
		dfs(u);
	}
}
void ddfs(int x) {
	vis[x] = true;
	for (int i = 0; i < v[x].size(); i++) {
		int u = v[x][i];
		if (vis[u]) continue;
		ddfs(u);
	}
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= m; i++) vi[i] = false;
	int x, y;
	string s;
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		cin >> s;
		if (s == "Lun") {
			add(x, y, 1);
			add(y, x, 1);
		}
		if (s == "Qie") {
			add(x, y, 2);
			add(y, x, 2);
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!dfn[i]) Tarjan(i, 0);
	}
	for (int i = 1; i <= n; i++) {
		if (!belog[i]) {
			ecc++;
			dfs(i);
		}
	}
	for (int i = 1; i <= m; i++) {
		if (belog[e[i * 2].f] == belog[e[i * 2].t] && e[i * 2].w == 1) {
			vi[i] = true;
		}
	}
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= m; i++) {
		int aa = find(belog[e[i * 2].f]);
		int bb = find(belog[e[i * 2].t]);
		if (aa != bb && e[i * 2].w == 2) {
			fa[aa] = bb;
			vi[i] = true;
		}
	}
	int sum = 0;
	for (int i = 1; i <= m; i++) {
		if (vi[i]) {
			sum++;
			v[e[i * 2].f].push_back(e[i * 2].t);
			v[e[i * 2].t].push_back(e[i * 2].f);
		}
	}
	memset(vis, 0, sizeof(vis));
	ddfs(1);
	for (int i = 1; i <= n; i++) {
		if (!vis[i]) {
			cout << "NO";
			return 0;
		}
	}
	cout << "YES" << '\n';
	cout << sum << '\n';
	for (int i = 1; i <= m; i++) {
		if (vi[i]) {
			cout << e[i * 2].f << ' ' << e[i * 2].t << '\n';
		}
	}
	return 0;
}

魔卡少女樱 10 pts

这种题一般都是DP,而且还要加上组合数之类的;

考虑对原数组 a 进行差分得到数组 b ,(特别的,b1=a1),那么我们可以发现,如果要满足同余的条件,那么除了 b1 可以为 0 以外,其它的只能取 23

可以发现,bm (这就相当于 bnm);

所以我们枚举 b1 的三种取值,将其设为 x

设当前选了 k2,那么我们就很容易得出当前有多少个 1

那么现在,我们要对数组 b 进行加 3 的操作,而我们要求的就是这个的方案数;

设我们每个 b 管辖它们前面的 3(即它前面的 3 都要加到这个 b 上),那么我们现在一共最多能填 m(n+(x1)+k)33 (就是用 m 减去当前序列的总和),现在一共有 n+m(n+(x1)+k)3 个空位可以供我们填 3b,但是最后一位只能填 b,所以一共有 n+m(n+(x1)+k)31 个位置我们可以填 3,那么只需枚举一下填 3 的个数即可用组合数求解;

当然,最后还要考虑 2 的位置,显然一共有 Cn1k 种方案;

所以,我们的答案为:

a=02k=0n1Cn1k×j=0n+m(n+(x1)+k)31Cn+m(n+(x1)+k)31j

要注意当原序列的和小于零时终止循环,进行下一个 a 的判定;

发现最内层的 j=0n+m(n+(x1)+k)31Cn+m(n+(x1)+k)31j 可以前缀和处理,所以时间复杂度为 O(n)

点击查看代码
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
const long long mod = 998244353;
int n, m;
long long fac[20000005], fav[20000005];
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 (m == 0) return 1;
	if (n == 0) return 0;
	if (m > n) return 0;
	if (m == n) return 1;
	return fac[n] * fav[m] % mod * fav[n - m] % mod;
}
long long sum[20000005];
long long ans;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	fac[0] = 1;
	fav[0] = 1;
	for (int i = 1; i <= max(n, m); i++) {
		fac[i] = fac[i - 1] * i % mod;
		fav[i] = ksm(fac[i], mod - 2);
	}
	sum[0] = C(n - 1, 0);
	for (int i = 1; i <= max(n, m); i++) {
		sum[i] = (sum[i - 1] + C(n + i - 1, i)) % mod;
	}
	int cnt = 0;
	for (int a = 0; a <= 2; a++) {
		for (int k = 0; k <= n - 1 && (n + (a - 1) + k) <= m; k++) {
			cnt = (m - (n + (a - 1) + k)) / 3;
			ans = (ans + C(n - 1, k) * sum[cnt] % mod) % mod;
		}
	}
	cout << ans;
	return 0;
}
posted @   Peppa_Even_Pig  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示