[赛记] 暑假集训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,而且还要加上组合数之类的;
考虑对原数组
可以发现,
所以我们枚举
设当前选了
那么现在,我们要对数组
设我们每个
当然,最后还要考虑
所以,我们的答案为:
要注意当原序列的和小于零时终止循环,进行下一个
发现最内层的
点击查看代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】