[考试记录] 2024.11.7 noip模拟赛7
基础暴力分 300pts 🤡
T1 图书管理
枚举每个左端点,维护一个中位数指针和一个桶,每次 push 进来两个数:
- 两个数都大于当前中位数:中位数增大
- 两个数都小于当前中位数:中位数减小
- 两个数一大一小:中位数不变
极限数据可以卡到 。
#include<bits/stdc++.h> using namespace std; #define int long long constexpr int N = 1e4 + 5; int n, p[N], val[N], ans; signed main(){ freopen("book.in", "r", stdin); freopen("book.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1; i<=n; ++i) cin>>p[i]; for(int i=1; i<=n; ++i){ int mid = 0; for(int j=i; j<=n; j+=2){ ++val[p[j]]; if(j == i){ mid = p[j]; ans += i * j * mid; } else { ++val[p[j-1]]; if(p[j-1] < mid && p[j] < mid){ --mid; while(!val[mid]) --mid; } else if(p[j-1] > mid && p[j] > mid){ ++mid; while(!val[mid]) ++mid; } ans += i * j * mid; } } for(int j=i; j<=n; ++j) val[p[j]] = 0; } cout<<ans; return 0; }
T2 两棵树
暴力 + 输出大样例 = 40pts
联通块数=剩余点数 - 剩余变数
那么贡献就可以拆成:点*点-边*点-点*边+边*边
那么就可以分成三种情况讨论:
- 点*点:,,当 均保留的概率为 ,而 的概率为 。所以期望为 。
- 边*点:选择 ,,并且均保留的概率为 ,其余均为 。所以期望为 。
- 边*边:选择 ,,当 互不相同时,概率为 ,其余均为 。那么枚举 中的边,计算符合条件的 中的边: 减去 连接的边数和 连接的边数,如果存在 则加上一。
#include<bits/stdc++.h> using namespace std; #define ll long long constexpr int N = 2e5 + 5, M = 998244353, Inv2 = 499122177, Inv16 = 935854081; int n, ans, g[N]; unordered_map<ll, bool> mp; struct edge{ int u, v; }e[N]; inline int qpow(int a, int k){ int res = 1; while(k){ if(k & 1) res = (ll)res * a % M; a = (ll)a * a % M; k >>= 1; } return res; } inline int mul(initializer_list<int> Mul){ int res = 1; for(int v : Mul) res = (ll)res * v % M; return res; } inline int add(initializer_list<int> Add){ int res = 0; for(int v : Add) res = res + v >= M ? res + v - M : res + v; return res; } inline int mod(int x){ return (x % M + M) % M; } int main(){ freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1; i<n; ++i) cin>>e[i].u>>e[i].v; for(int i=1, u, v; i<n; ++i){ cin>>u>>v, ++g[u], ++g[v]; if(u > v) swap(u, v); mp[(ll)u*N+(ll)v] = 1; } ans = mul({n-1, Inv2}); for(int i=1; i<n; ++i){ int u = e[i].u, v = e[i].v; if(u > v) swap(u, v); ans = add({ans, mul({add({n-1, -g[u], -g[v], mp[(ll)u*N+(ll)v]}), Inv16})}); } return cout<<ans, 0; }
T3 函数
过百万,暴力碾标算
歪解。
考虑两种暴力:
- 从左向右枚举每一个数检查即可,复杂度 。
- 枚举 每一个值,将这个值异或 之后看存不存在,如果存在则判断这个数的前一位或者后一位异或 是否大于 即可。复杂度 。
结合起来就能艹过去,并且最优解。数据太水。
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e6 + 5; int n, q, a[N]; unordered_map<int, int> mp; int main(){ freopen("fun.in", "r", stdin); freopen("fun.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>q; for(int i=1; i<=n; ++i) cin>>a[i], mp[a[i]] = i; int A, B; while(q--){ cin>>A>>B; bool ok = 1; if(n <= B){ for(int i=1; i<n; ++i) if(((a[i]^A)<=B && (a[i+1]^A)>=B) || ((a[i]^A)>=B && (a[i+1]^A)<=B)) { cout<<i<<'\n'; ok = 0; break; } } else { for(int i=0; i<=B; ++i){ int id = mp[i^A]; if(!id) continue; if(id < n && (a[id+1] ^ A) >= B){ cout<<id<<'\n'; ok = 0; break; } if(id > 1 && (a[id-1] ^ A) >= B){ cout<<(id-1)<<'\n'; ok = 0; break; } } } if(ok) cout<<"-1\n"; } return 0; }
正解
30pts
枚举所有情况。
60pts
通过整体二分,判定 内是否有解的方式找到第一组解,或利用离线数据结构技巧进行求解,复杂度为 ,与正解关联性不大。
100pts
求解 最小和最大的两个位置 ,如果 显然无解。
否则每次取 中点 ,因为 异号,所以 和 必然有一对异号,每次区间长度减半,因此重复 次即可。
求解 最小和最大的两个位置 可以利用 trie 快速求解,时间复杂度为
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e6 + 5, T = 3e7 + 5; int n, q, li[N]; namespace Trie{ int Id[T], sn[T][2], cnt; inline void insert(int x, int id){ int p = 0; bool b = 0; for(int i=29; i>=0; --i){ b = (x >> i) & 1; if(!sn[p][b]) sn[p][b] = ++cnt; p = sn[p][b]; } Id[p] = id; } inline int query(int a, int b){ int p1 = 0, p2 = 0, l, r; bool bt = 0; for(int i=29; i>=0; --i){ bt = (a >> i) & 1; if(sn[p1][bt]) p1 = sn[p1][bt]; else p1 = sn[p1][bt^1]; if(sn[p2][bt^1]) p2 = sn[p2][bt^1]; else p2 = sn[p2][bt]; } l = Id[p1], r = Id[p2]; if(l > r) swap(l, r); int numl = (li[l] ^ a) - b, numr = (li[r] ^ a) - b; if((long long)numl * numr > 0) return -1; if(abs(l - r) == 1) return min(l, r); int mid, numd; while(l < r-1){ mid = (l + r) >> 1, numd = (li[mid] ^ a) - b; if((long long)numd * numl > 0) l = mid, numl = numd; else r = mid, numr = numd; } return l; } } int main(){ freopen("fun.in", "r", stdin); freopen("fun.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>q; for(int i=1; i<=n; ++i) cin>>li[i], Trie::insert(li[i], i); int a, b; while(q--) cin>>a>>b, cout<<Trie::query(a, b)<<'\n'; return 0; }
编辑
不可以进行修改,然后获得 60pts……🤬
考虑枚举 的每一个后缀,令左端点为 ,那么令 表示最大的 ,使得 和 可以在 次编辑内得到,那么这样编辑形成的匹配,它的编辑距离一定是最短的。
那么考虑转移。既然要找到最大的 ,那么可以利用 Hash + 二分求出。
考虑下一步操作:增加和修改都会导致匹配长度加长,但是增加不会改变最大的 的位置,但是修改会使最大的 加一。对于删除, 是会减小的,但是为了方便统计答案,所以不减。
统计答案时就统计 的状态数,注意判断是否合法。
最后统计的答案其实是个前缀和状物,减一下即可。
#include<bits/stdc++.h> using namespace std; #define ull uint64_t constexpr int N = 5e4 + 5, Inf = -1e9; int k, lens, lent, ans[35], f[35][65]; string S, T; ull p[N], hs[N], ht[N]; inline ull geth(ull *h, int l, int r){ return h[r] - h[l-1] * p[r-l+1]; } inline int lct(int l1, int l2){ if(l1 < 0 || l2 < 0 || l1 > lens || l2 > lent || S[l1-1] ^ T[l2-1]) return 0; int l = 0, r = min(lens-l1, lent-l2), mid; while(l < r){ mid = (l + r + 1) >> 1; if(geth(hs, l1, l1+mid) ^ geth(ht, l2, l2+mid)) r = mid - 1; else l = mid; } return l + 1; } int main(){ freopen("edit.in", "r", stdin); freopen("edit.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>k>>S>>T; p[0] = 1; lens = S.size(), lent = T.size(); for(int i=1; i<=lent; ++i) p[i] = p[i-1] * 131; for(int i=1; i<=lens; ++i) hs[i] = hs[i-1] * 131 + S[i-1] - 'a'; for(int i=1; i<=lent; ++i) ht[i] = ht[i-1] * 131 + T[i-1] - 'a'; for(int l=1; l<=lent; ++l){ for(int i=0; i<=k; ++i) for(int j=0; j<=k<<1; ++j) f[i][j] = Inf; f[0][k] = 0; for(int i=0; i<=k; ++i) for(int j=-k; j<=k; ++j) if(f[i][j+k] ^ Inf){ f[i][j+k] += lct(f[i][j+k]+1, l+f[i][j+k]+j); if(f[i][j+k] >= lens && j+lens > 0 && j+lens <= lent-l+1) ++ans[i]; f[i+1][j+k] = max(f[i+1][j+k], f[i][j+k]+1); if(j != -k) f[i+1][j+k-1] = max(f[i+1][j+k-1], f[i][j+k]+1); if(j != k) f[i+1][j+k+1] = max(f[i+1][j+k+1], f[i][j+k]); } } for(int i=0; i<=k; ++i) cout<<(ans[i]-ans[i-1])<<'\n'; return 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18533849
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步