[考试记录] 2024.11.12 noip模拟赛11
T1 送信卒
使用 记录走到 的路径的横边和竖边的数量,然后取 。这里取 的原因是,找到的路径必须是最短路,当 取的小的时候竖边就会变多,所以这条路径就不一定是最短路了。
#include<bits/stdc++.h> using namespace std; #define p pair<int, int> int n, m, sx, sy, tx, ty; int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; bitset<105> mp[105], vis[105]; double s; struct node{ int x, y, w, h; }; queue<node> q; vector<p> vec; int main(){ freopen("msg.in", "r", stdin); freopen("msg.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>m>>sx>>sy>>tx>>ty; for(int i=1, x; i<=n; ++i) for(int j=1; j<=m; ++j) cin>>x, mp[i][j] = x; cin>>s; q.emplace(node{sx, sy, 0, 0}); while(!q.empty()){ auto t = q.front(); q.pop(); int ux = t.x, uy = t.y; vis[ux][uy] = 1; for(int i=0; i<4; ++i){ int vx = ux + dir[i][0], vy = uy + dir[i][1]; if(vx < 1 || vy < 1 || vx > n || vy > m || mp[vx][vy] || vis[vx][vy]) continue; int tmpw = t.w, tmph = t.h; dir[i][0] ? ++tmph : ++tmpw; if(vx == tx && vy == ty){ vec.emplace_back(p{tmpw, tmph}); continue; } vis[vx][vy] = 1; q.emplace(node{vx, vy, tmpw, tmph}); } } double ans = 0.0; for(auto it : vec){ int w = it.first, h = it.second; if(w * 1.0 > s) continue; ans = max(ans, 1.0 * (s - 1.0 * w) / h); } return cout<<fixed<<setprecision(3)<<ans, 0; }
T2 共轭树图
看起来很屌的一道题,考场上被吓住了。令 表示节点 子树内节点向前 个祖先连边的贡献。那么有
使用前缀和即可做到 。
#include<bits/stdc++.h> using namespace std; #define ll long long constexpr int N = 3e3 + 5, M = 998244353; int n, dep[N], f[N][N]; vector<int> G[N]; 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 mul(initializer_list<int> Mul){ int res = 1; for(int v : Mul) res = (ll)res * v % M; return res; } inline void dfs1(int u, int fa){ for(int v : G[u]) if(v ^ fa) dep[v] = dep[u] + 1, dfs1(v, u); } inline void dfs2(int u, int fa){ if((u ^ n) && G[u].size() == 1){ for(int i=1; i<=dep[u]; ++i) f[u][i] = i; return; } vector<int> tmp(dep[u]+3, 1); for(int v : G[u]) if(v ^ fa){ dfs2(v, u); for(int i=2; i<=dep[v]; ++i) tmp[i] = mul({tmp[i], f[v][i]}); } for(int i=1; i<=dep[u]; ++i) f[u][i] = add({f[u][i-1], tmp[i+1]}); } int main(){ freopen("reflection.in", "r", stdin); freopen("reflection.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1, u, v; i<n; ++i){ cin>>u>>v; G[u].emplace_back(v), G[v].emplace_back(u); } dep[n] = 1, dfs1(n, 0), dfs2(n, 0); int ans = 1; for(int v : G[n]) ans = mul({ans, f[v][1]}); return cout<<ans, 0; }
T3 摸鱼军训
考虑每一次冒泡都会把一个大数提到序列最后面,那么相应地就会有较小的数向前移动。那么就可以大致描述某一个数的移动过程:先向前走一段,再向后走一段。显然有每个数向前走的距离为这个数前面比它大的数的数量,称为 ,如果查询的 要小于等于 ,那么答案即为 。
对于向后走的一段,考虑使用一个队列来更直观的维护这个东西。每一次冒泡之前都将向前走走完的数加到队列中去,这个队列一定是单调递增的。对于如下队列:(中间加点是为了凸显在原数列中的位置)
那么下一次冒泡,序列变化如下:
可以发现的是每一次冒泡,队列里的每一个数,都会移动到它的下一个数的屁股后面。又因为队列里的数的顺序是按照向前移动完的时间加进去的,那么比 大的数一定会先加入队列中。那么对于第一次移动,会移动到 第一个大于它的数 的屁股后面,也就是 ,第二次移动时, 会移动到 的屁股后面,所以 ,然后 移动到 的后面 。规律显然。对于第 次移动,在队列里第 个的数的位置为 ,那么答案即为 。又因为在队列里比他靠前的数只能是比它大的数,那么就在原序列里找从左往右数第 个大于它的数的 即可。
#include<bits/stdc++.h> using namespace std; constexpr int B = 1<<20; char buf[B], *p1 = buf, *p2 = buf, obuf[B], *O = obuf; #define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++) template <typename T> inline void rd(T &x){ x = 0; int f = 0; char ch = gt(); for(; !isdigit(ch); ch = gt()) f ^= ch == '-'; for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48); x = f ? -x : x; } template <typename T, typename ...TT> inline void rd(T &x, TT &...xx){ rd(x), rd(xx...); } #define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch)) template <typename T> inline void wt(T x){ if(x > 9) wt(x / 10); pt(x % 10 ^ 48); } #define fw fwrite(obuf, 1, O - obuf, stdout) #define p pair<int, int> constexpr int N = 5e5 + 5; int n, q, a[N], ans[N], pos[N], lm[N]; vector<p> Q[N]; namespace BIT{ #define lb(x) ((x) & (-x)) int t[N]; inline void add(int pos, int val){ for(; pos<=n; pos+=lb(pos)) t[pos] += val; } inline int query(int pos){ int res = 0; for(; pos>0; pos-=lb(pos)) res += t[pos]; return res; } } namespace ST{ #define ls (id << 1) #define rs (id << 1 | 1) struct node{ int l, r, sum; }t[N<<2]; inline void build(int id, int l, int r){ t[id] = node{l, r, 0}; if(l == r) return; int mid = (l + r) >> 1; build(ls, l, mid), build(rs, mid+1, r); } inline void modify(int id, int pos){ if(t[id].l == t[id].r) return t[id].sum = 1, void(); int mid = (t[id].l + t[id].r) >> 1; modify((pos <= mid) ? ls : rs, pos); t[id].sum = t[ls].sum + t[rs].sum; } inline int query(int id, int val){ if(t[id].l == t[id].r) return t[id].l; int mid = (t[id].l + t[id].r) >> 1; if(t[ls].sum >= val) return query(ls, val); return query(rs, val-t[ls].sum); } } int main(){ freopen("bubble.in", "r", stdin); freopen("bubble.out", "w", stdout); rd(n); for(int i=1; i<=n; ++i){ rd(a[i]), BIT::add(a[i], 1); lm[a[i]] = i - BIT::query(a[i]); pos[a[i]] = i; } rd(q); for(int i=1, k, x; i<=q; ++i){ rd(k, x); if(lm[x] >= k) ans[i] = pos[x] - k; else if(n - x < k) ans[i] = x; else Q[x].emplace_back(p{k, i}); } ST::build(1, 1, n); for(int i=n; i>=1; --i){ for(auto it : Q[i]){ int ps = ST::query(1, it.first); ans[it.second] = ps - it.first; } ST::modify(1, pos[i]); } for(int i=1; i<=q; ++i) wt(ans[i]), pt(10); return fw, 0; }
T4 神奇园艺师
首先,如果将这个子集中每个数的质因子 指数提取出来并排个序,那么最终的序列里 的指数必然其为中位数,操作次数即为 。
考虑将贡献拆解。将整个序列的指数 提取出来排个序,枚举第 位,考虑第 个数的指数 能给答案做出怎样的贡献。不妨考虑在 左边选择 个数,右边选择 个数。因为是有序的,所以当 的时候中位数一定在 里边取得,设中位数为 ,那么应有 ,但可以发现的是,中位数的贡献是一定可以拆绝对值之后左右消掉的,所以贡献应为 。复杂度 。
最后的优化需要范德蒙德卷积,没写完……
#include<bits/stdc++.h> using namespace std; constexpr int B = 1<<20; char buf[B], *p1 = buf, *p2 = buf, obuf[B], *O = obuf; #define gt() (p1==p2 && (p2=(p1=buf)+fread(buf, 1, B, stdin), p1==p2) ? EOF : *p1++) template <typename T> inline void rd(T &x){ x = 0; int f = 0; char ch = gt(); for(; !isdigit(ch); ch = gt()) f ^= ch == '-'; for(; isdigit(ch); ch = gt()) x = (x<<1) + (x<<3) + (ch^48); x = f ? -x : x; } template <typename T, typename ...TT> inline void rd(T &x, TT &...xx){ rd(x), rd(xx...); } #define pt(ch) (O-obuf==B && (fwrite(obuf, 1, B, stdout), O=obuf), *O++ = (ch)) template <typename T> inline void wt(T x){ if(x > 9) wt(x / 10); pt(x % 10 ^ 48); } #define fw fwrite(obuf, 1, O - obuf, stdout) constexpr int N = 1e6 + 5, M = 1e9 + 7, PM = 8e4; int n, a[N], inv[N], fac[N], mx, dv[N], s[N]; vector<int> pr, P[N]; bitset<N> vis; inline int qpow(int a, int k){ int res = 1; while(k){ if(k & 1) res = (long long)res * a % M; a = (long long)a * a % M; k >>= 1; } 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 mul(initializer_list<int> Mul){ int res = 1; for(int v : Mul) res = (long long)res * v % M; return res; } inline int mod(int x){ return (x + M) % M; } inline int C(int a, int b){ return b > a ? 0 : mul({fac[a], inv[b], inv[a-b]}); } int main(){ freopen("game.in", "r", stdin); freopen("game.out", "w", stdout); rd(n); for(int i=1; i<=n; ++i) rd(a[i]), mx = max(mx, a[i]); fac[0] = inv[0] = 1; for(int i=1; i<=n; ++i) fac[i] = mul({fac[i-1], i}); inv[n] = qpow(fac[n], M-2); for(int i=n-1; i>=1; --i) inv[i] = mul({inv[i+1], i+1}); s[0] = C(n-1, 0); for(int i=1; i<n; ++i) s[i] = add({s[i-1], C(n-1, i)}); for(int i=2; i<=mx; ++i){ if(!vis[i]) pr.emplace_back(i), dv[i] = i; for(int v : pr){ if((long long)v * i > mx) break; vis[v*i] = 1; dv[v*i] = v; if(!(i % v)) break; } } for(int i=1; i<=n; ++i){ int v = a[i]; while(v > 1){ int p = dv[v], cnt = 0; while(!(v % p)) ++cnt, v /= p; P[p].emplace_back(cnt); } } int ans = 0; for(int v : pr) if(!P[v].empty()){ sort(P[v].begin(), P[v].end()); for(int j=n-P[v].size()+1, ps=0; j<=n; ++j, ++ps){ int res = add({(n-j-1 >= 0 ? mod(-s[n-j-1]) : 0), s[j-2]}); ans = add({ans, mul({res, P[v][ps]})}); } } return cout<<ans, 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18542463
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步