[考试记录] 2024.9.22 csp-s模拟赛31
T1 自然数
手玩数据可以知道,对于 这样的每次删去后面数的序列的 值是单调不降的。并且每次删去前面一个数 的时候,产生影响的区间只是那些只有一个 的序列,影响是如果该区间的 那么就把它设置为 。
so,先 维护出原始序列每次删后面的一个数的 mex,作为线段树 个节点的值。再预处理出每个 再序列中的位置 ,那么修改的区间就是 和 序列里第一个 的位置取交集。
需要维护区间最大值,区间和,区间修改。
#include<bits/stdc++.h> using namespace std; #define int long long constexpr int N = 2e5 + 5; int n, a[N], b[N], pos[N], mx, bas[N], ans, s, cnt[N]; stack<int> P[N]; namespace ST{ #define ls (id << 1) #define rs (id << 1 | 1) struct node{ int l, r, sum, mx, tag; }t[N<<2]; inline void pushup(int id){ t[id].mx = max(t[ls].mx, t[rs].mx); t[id].sum = t[ls].sum + t[rs].sum; } inline void addtag(int id, int val){ t[id].sum = val * (t[id].r - t[id].l + 1); t[id].mx = t[id].tag = val; } inline void pushdown(int id){ if(t[id].tag != -1) addtag(ls, t[id].tag), addtag(rs, t[id].tag); t[id].tag = -1; } inline void build(int id, int l, int r){ t[id].l = l, t[id].r = r, t[id].tag = -1; if(l == r) return (void)(t[id].mx = t[id].sum = bas[l]); int mid = (l + r) >> 1; build(ls, l, mid), build(rs, mid+1, r); pushup(id); } inline void modify(int id, int l, int r, int val){ if(l > r) return; if(l <= t[id].l && t[id].r <= r) return addtag(id, val); pushdown(id); int mid = (t[id].l + t[id].r) >> 1; if(l <= mid) modify(ls, l, r, val); if(r > mid) modify(rs, l, r, val); pushup(id); } inline int find(int id, int val){ if(t[id].l == t[id].r) return t[id].l; pushdown(id); if(t[ls].mx >= val) return find(ls, val); else return find(rs, val); } } signed main(){ freopen("mex.in", "r", stdin), freopen("mex.out", "w", stdout); ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1; i<=n; ++i) cin>>a[i], b[i] = a[i]; sort(b+1, b+1+n); int len = unique(b+1, b+1+n) - b - 1; for(int i=0; i<=len; ++i) if(b[i+1] != i){ bas[n] = i; break; } for(int i=1; i<=n; ++i) P[i].push(n+1); for(int i=n; i>=1; --i){ pos[i] = lower_bound(b+1, b+1+len, a[i]) - b; ++cnt[pos[i]]; P[pos[i]].push(i); } for(int j=n-1; j>=1; --j){ bas[j] = bas[j+1]; --cnt[pos[j+1]]; if(b[pos[j+1]] < bas[j] && !cnt[pos[j+1]]) bas[j] = b[pos[j+1]]; } ST::build(1, 1, n+1); ans += ST::t[1].sum; for(int i=1; i<=n; ++i){ ST::modify(1, i, i, 0); int l = ST::find(1, a[i]), r = P[pos[i]].top(); P[pos[i]].pop(); int r2 = P[pos[i]].top() - 1; ST::modify(1, max({l, r, i}), r2, a[i]); ans += ST::t[1].sum; } return cout<<ans, 0; }
T2 钱仓
每 mol 钱只能运一次,可以知道是贪心了。由题目可知,在这个环里一定存在某一个断点,使得这个断点之前的钱运不到断点处。也就是从断点之后开始枚举转移可以铺满所有位置,这个断点不唯一,找到一个即可。考虑每个点的钱往哪里运,肯定是往距离自己最小的地方(包括自己)运。那就把每个点加到队列里去,先进队列的先运出去即可。复杂度
#include<bits/stdc++.h> using namespace std; constexpr int B = 1 << 23; 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; } #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 < 0) pt('-'), x = -x; if(x >= 10) wt(x / 10); pt(x % 10 ^ 48); } #define fw fwrite(obuf, 1, O - obuf, stdout) #define int long long constexpr int N = 2e5 + 5; int n, c[N], sum, bg, pos = 1, mx, ans; queue<int> q; signed main(){ freopen("barn.in", "r", stdin), freopen("barn.out", "w", stdout); rd(n); for(int i=1; i<=n; ++i) rd(c[i]), c[i+n] = c[i]; for(int i=1; i<=n; ++i){ sum += c[i] - 1; if(sum < mx) mx = sum, bg = i+1; } for(int i=bg; i<=bg+n-1; ++i){ while(c[i]--) q.push(i); int top = q.front(); q.pop(); ans += (i - top) * (i - top); } return cout<<ans, 0; }
T3 游戏
solution 1
对于 。
100 pts
令 表示 Alice 先手取胜的概率, 表示 Alice 后手取胜的概率。
-
对于 的情况,显然两人都取正面最优,所以有:
化简得:
可以得到转矩阵:
将 得:
所以这种情况下他俩的大小关系会交换。
-
对于 的情况:
化简得:
可以得到转移矩阵:
将 得:
所以这种情况下两者关系也会互换。
所以每进行一次递推,大小关系会互换。具体地,当前递推奇数次的时候,最后一次为情况1, 反之为情况2。根据矩阵乘法的结合律,使用矩阵快速幂即可,复杂度 。
#include<bits/stdc++.h> using namespace std; #define ll long long constexpr int M = 1e9 + 7, Inv = 571428574; struct Mate{ int m[2][2]; }P, Q, bas; Mate operator * (const Mate &a, const Mate &b){ Mate c; memset(c.m, 0, sizeof(c.m)); for(int i=0; i<2; ++i) for(int j=0; j<2; ++j) for(int k=0; k<2; ++k) c.m[i][j] = ((ll)a.m[i][k] * b.m[k][j] % M + c.m[i][j]) % M; return c; } Mate qpow(Mate a, int k){ Mate ans = a; --k; while(k){ if(k & 1) ans = ans * a; a = a * a; k >>= 1; } return ans; } int qpow(int a, int k){ int ans = 1; while(k){ if(k & 1) ans = (ll)ans * a % M; a = (ll)a * a % M; k >>= 1; } return ans; } int t, n, p, q; int main(){ freopen("game.in", "r", stdin), freopen("game.out", "w", stdout); ios::sync_with_stdio(0), cout.tie(0), cout.tie(0); cin>>t; while(t--){ cin>>n>>p>>q; p = (ll)p * Inv % M, q = (ll)q * Inv % M; int fp = ((1 - p) % M + M) % M, fq = ((1 - q) % M + M) % M; int inv = (((ll)p + (ll)q - (ll)p * q % M) % M + M) % M; int finv = ((1ll - (ll)p * q % M) % M + M) % M; inv = qpow(inv, M - 2); finv = qpow(finv, M - 2); P.m[0][0] = (ll)q * fp % M * inv % M; P.m[0][1] = (ll)q * inv % M; P.m[1][0] = (ll)p * inv % M; P.m[1][1] = (ll)p * fq % M * inv % M; Q.m[0][0] = (ll)p * fq % M * finv % M; Q.m[0][1] = (ll)fq * finv % M; Q.m[1][0] = (ll)fp * finv % M; Q.m[1][1] = (ll)q * fp % M * finv % M; bas.m[0][0] = 0, bas.m[0][1] = 1; if(n>>1) bas = bas * qpow(P*Q, n>>1); if(n & 1) bas = bas * P; cout<<bas.m[0][0]<<'\n'; } return 0; }
T4 暴雨
改不出来了~ 咕
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18425888
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步