SM 集训记录(2024.11.15 ~ 2024.11.29)
DAY0(2024.11.15)
终于来了www
T2 GYM104787M
首先定义一个副本连通块是只经过编号
然后假如有
关于合法方案的判定
在该题中,一个方案不合法当且仅当两个叶子构成了一个环或者一个点与两个树都不联通。所以这就对应着原树中有两个叶子在一个连通块。反之,该方案合法。- 这启发我们判断充要条件时可以从反面思考。
细节
这里的设
初始状态:叶子中:
当
qwq
#include<bits/stdc++.h> #define ll long long #define pb emplace_back using namespace std; const int N = 5000 + 10; const ll mod = 998244353; struct edge{ int v, next; }edges[N << 1]; int head[N], idx; int n; bool chosen[N], vis[N]; ll f[N], g[N], pw2[N]; void add_edge(int u, int v){ edges[++idx] = {v, head[u]}; head[u] = idx; } int lfcnt; void dfs(int u, int fa){ //cout << u << " " << fa << "\n"; if(!chosen[u]){lfcnt++; f[u] = 1; g[u] = 0; return;} f[u] = 0; g[u] = 1; vis[u] = 1; for(int i = head[u]; i; i = edges[i].next){ int v = edges[i].v; if(v == fa) continue; dfs(v, u); f[u] = (f[u] * (g[v] + f[v]) % mod + f[v] * g[u] % mod) % mod; g[u] = (g[u] * (g[v] + f[v])) % mod; //cout << u << " " << v << " " << f[u] << " " << g[u] << "\n"; } } signed main(){ freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; pw2[0] = 1; for(int i = 1; i <= n; i++) pw2[i] = 2ll * pw2[i - 1] % mod; for(int i = 1; i < n; i++){ int x, y; cin >> x >> y; add_edge(x, y); add_edge(y, x); } for(int i = 1; i < n; i++){ int x; cin >> x; chosen[x] = 1; for(int u = 1; u <= n; u++) vis[u] = 0, f[u] = g[u] = 0; ll ans = 1; for(int u = 1; u <= n; u++) if(!vis[u] && chosen[u]) lfcnt = 0, dfs(u, 0), ans = (ans * f[u] % mod * pw2[lfcnt - 1] % mod) % mod; cout << ans % mod << "\n"; } return 0; }
T3 Mod
- Statement
给定长度为
void mod(int x){ for(int i=1;i<=n;i++)a[i]=a[i]%x; }
你可以执行这个函数任意次,且每次调用函数的参数可以任意指定,但要求调用的参数
对于
- Solution
首先考虑特殊性质的做法,手玩可以发现,操作过的序列一定形如
于是不难发现这也是充要条件,因为我们可以把所有
关于 DP 以及为什么不会算重
首先给出
枚举第一个
转移如下:
qwq
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 500 + 10, mod = 998244353; int n, a[N], V, vis[N]; ll f[N][N]; void ADD(ll &x, ll y){x = (x + y) % mod;} ll solve(int s){ memset(f, 0, sizeof f); f[s][0] = 1; int lst = 0; for(int i = 1; i <= V; i++){ if(vis[i]){ ll sum = 0; for(int j = 0; j < s; j++) ADD(sum, f[lst][j]); for(int j = 0; j <= i - lst - 1; j++) ADD(f[i][j], sum); lst = i; } for(int j = 0; j < s; j++) ADD(f[i + 1][j + 1], f[i][j]); } ll ret = 0; for(int i = 0; i < s; i++) ADD(ret, f[V][i]); return ret; } signed main(){ freopen("mod.in", "r", stdin); freopen("mod.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n; ll ans = 0; for(int i = 1; i <= n; i++) cin >> a[i], V = max(V, a[i]), vis[a[i]] = 1; for(int i = 1; i <= V + 1; i++) ADD(ans, solve(i)); cout << (ans + 1) % mod; return 0; }
DAY1
早上打了一场信心赛,AK 了。
下午补了
DAY2
晚上尝试和
DAY3
早上先补了两个题,然后写了一个
感觉效率好低。
傍晚写了一下博客。晚上又打模拟赛,T1 签到,T2 子集容斥,T3 线性基板子题但我不会qwq,T4 神秘,最后
DAY4
早上学了线性基。this
下午做里面的练习题。晚上模拟赛,
T2 string
- Statement:
给定
- Solution:
不妨把所有操作都放到
假设现在走到节点
qwq
#include<bits/stdc++.h> #define ll long long #define db double using namespace std; const int N = 2e6 + 10, M = 1e3 + 10; db ans = 1, f[M][M]; struct Trie{ int son[N][2], siz[N], tot = 1; void ins(string str) { int p = 1; for(int i = 0; i < str.size(); i++) { int id = str[i] - '0'; if(!son[p][id]) son[p][id] = ++tot; p = son[p][id]; siz[p]++; } } void solve(int u) { // cout << u << " " << siz[son[u][0]] << " " << siz[son[u][1]] << " " << f[siz[son[u][0]]][siz[son[u][1]]] << " " << ans << "\n"; if(!u) return; ans = ans * f[siz[son[u][0]]][siz[son[u][1]]]; solve(son[u][0]); solve(son[u][1]); } }tr; int n, m; signed main(){ // system("fc string6.out string.out"); freopen("string.in", "r", stdin); freopen("string.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); db tb; cin >> n >> m >> tb; tb = max(tb, 1 - tb); const db p = tb, ip = 1 - tb; for(int i = 1; i <= n; i++) { string str; cin >> str; tr.ins(str); } f[0][0] = 1; for(int i = 0; i <= n; i++) { for(int j = 0; j <= n; j++) { if(i == 0 && j == 0) continue; if(i > j) f[i][j] = f[i - 1][j] * p + (j == 0 ? 0.0 : (f[i][j - 1] * ip)); else f[i][j] = (j == 0 ? 0.0 : (f[i][j - 1] * p)) + (i == 0 ? 0.0 : (f[i - 1][j] * ip)); // cout << i << " " << j << " " << f[i][j] << "\n"; } } tr.solve(1); cout << fixed << setprecision(12) << ans; return 0; }
DAY5
写了一些线性基,补了两道
DAY6
直接变成暴力老哥,
DAY7
T3 match
- Statement:
由于魔法的世界在不断地变化,现在有若干的询问和修改的事件,每一次修改会交换两名选手在某一种场地上的排名。
每次询问,小
- Solution:
首先对于每个序列
那么维护第
考场上暴力
拿了 。这启发我们对于图论问题不一定需要观察整个图的形态,而只需要关注答案相关,然后转化即可。
qwq
#pragma GCC optimize(3, "Ofast", "inline") #include<bits/stdc++.h> #define int long long using namespace std; int min(int x, int y){return (x < y) ? x : y;} int max(int x, int y){return (x > y) ? x : y;} void chkmin(int& x, int y){if(x > y) x = y;} void chkmax(int& x, int y){if(x < y) x = y;} const int N = 1e5 + 10; int n, a[3][N], rk[3][N], Lmn[N], Rmx[N], ans; namespace Segtree{ #define ls (o << 1) #define rs (o << 1 | 1) #define mid (l + r >> 1) int tag[N << 2], tmin[N << 2]; void pushup(int o){tmin[o] = min(tmin[ls], tmin[rs]);} void addtag(int o, int v){tmin[o] += v; tag[o] += v;} void pushdown(int o){ if(!tag[o]) return; addtag(ls, tag[o]); addtag(rs, tag[o]); tag[o] = 0; } void Segadd(int o, int l, int r, int s, int t, int v){ if(s > t) return; if(s <= l && r <= t){addtag(o, v); return;} pushdown(o); if(s <= mid) Segadd(ls, l, mid, s, t, v); if(mid < t) Segadd(rs, mid + 1, r, s, t, v); pushup(o); } int fid(int o, int l, int r){ if(l == r) return l; pushdown(o); //cout << o << " " << l << " " << mid << " " << r << " " << tmin[ls] << "\n"; if(tmin[ls] == 0) return fid(ls, l, mid); return fid(rs, mid + 1, r); } } using namespace Segtree; void upd(int x, int rkqwq, int id){ // cout << x << " " << rkqwq << " " << id << "\n"; Segadd(1, 1, n, Lmn[x], Rmx[x] - 1, -1); rk[id][x] = rkqwq; Lmn[x] = Rmx[x] = rk[0][x]; for(int i = 1; i < 3; i++) chkmin(Lmn[x], rk[i][x]), chkmax(Rmx[x], rk[i][x]); Segadd(1, 1, n, Lmn[x], Rmx[x] - 1, 1); } signed main(){ freopen("match.in", "r", stdin); freopen("match.out", "w", stdout); // system("fc match.out match6.ans"); ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int T; cin >> n >> T; for(int id = 0; id < 3; id++){ for(int j = 1; j <= n; j++){ cin >> a[id][j]; rk[id][a[id][j]] = j; } } for(int i = 1; i <= n; i++){ Lmn[i] = Rmx[i] = rk[0][i]; for(int id = 1; id < 3; id++) chkmin(Lmn[i], rk[id][i]), chkmax(Rmx[i], rk[id][i]); Segadd(1, 1, n, Lmn[i], Rmx[i] - 1, 1); } ans = fid(1, 1, n); while(T--){ int opt; cin >> opt; if(opt == 2){ int id, x, y; cin >> id >> x >> y; id--; int rkx = rk[id][x], rky = rk[id][y]; upd(x, rky, id); upd(y, rkx, id); ans = fid(1, 1, n); //cout << ans << "\n"; } else{ int x; cin >> x; cout << (rk[0][x] <= ans ? "Yes" : "No") << "\n"; //cout << "Yes" << "\n"; } } return 0; }
T2 wolf
- Statement:
对于第
在
两种移动路径方案被认为不同,当且仅当存在某一轮移动中,某一只狼的移动方向不同。
- Solution:
不妨考虑一个点可以走到那些点,显然是一个斜的正方形,于是不妨将棋盘旋转
看到
较大的情况,可以通过一些方法将两维分开考虑。
DAY8
忘了。
11.26
T4
首先不难发现加入一个字符后,整个
那么不妨考虑使用树剖的思想,通过设置重儿子快速维护链上信息。然后考虑到这是
- 一个串所有的
可以被分为 个等差数列。
于是我们对于一个点
note
- Trie 整体异或可以直接打标记,整体
就从低位到高位贪心就行了。
本文作者:Little_corn
本文链接:https://www.cnblogs.com/little-corn/p/18551157
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步