CSP-S模拟13 [排序,Xorum,有趣的区间问题,无聊的卡牌问题](好久没更过考试总结了呢)
CSP-S模拟13
今天的破防除了考试保龄以外,还有自己的涩图题解传到了hsez的同学那里,属实是完完全全的社死了。
丢大人了啊。TST将会臭名昭著
A.排序
CF1375E Inversion SwapSort
给定长度为 的序列 ,求一种将每个逆序对下标 的排序,使依次交换每个 后, 不减。
其实考试题是弱化版,原题是一个序列,考试题是一个排列。序列会出现重复的数,会更难处理一些,我们先假定 是一个排列。
因为最后要 不减,所以最后每个数放在哪里是固定的。要求交换每个逆序对,我们可以 扫一遍求出所有的逆序对,记录下每组逆序对在 的位置。
之后去考虑每组逆序对交换的顺序。由于要交换逆序对的个数次,所以在每次交换后逆序对数只能减 ,即每一次交换只能影响当前交换的逆序对,不能影响其他的逆序对。比如:, 不能直接和 交换,这样 和 构成的逆序对都会被影响,交换次数就假掉了。
所以,我们要优先交换 值大的逆序对。详细一点,对于逆序对 ,在 构成的所有逆序对中,我们要先交换 小的。对于 不同,优先要交换 小的。
对于序列其实同理,只是会有相等的情况。同样,对于逆序对 ,如果 相同,先交换 小的。
Code
#include<cstdio> #include<vector> #include<algorithm> #define Pair pair< int, int > #define Make(x, y) make_pair(x, y) using namespace std; const int MAXN = 1010; int n, cnt; int num[MAXN], val[MAXN]; vector< Pair > out; bool cmp(const Pair &x, const Pair &y){ if(x.first == y.first){ if(num[x.second] != num[y.second]) return num[x.second] > num[y.second]; return x.second > y.second; } return x.first < y.first; } inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int main(){ n = read(); for(register int i = 1; i <= n; i++){ num[i] = read(); val[i] = num[i]; } sort(val + 1, val + 1 + n); cnt = unique(val + 1, val + 1 + n) - val - 1; for(register int i = 1; i <= n; i++){ int pos = lower_bound(val + 1, val + 1 + cnt, num[i]) - val; num[i] = pos; } for(register int i = 1; i <= n; i++) for(register int j = i + 1; j <= n; j++) if(num[i] > num[j]) out.push_back(Make(i, j)); sort(out.begin(), out.end(), cmp); printf("%ld\n", out.size()); for(register int i = 0; i < out.size(); i++) printf("%d %d\n", out[i].first, out[i].second); return 0; }
B.Xorsum
CF1427E Xum
给出的 一定为奇数,则它二进制表示下的最低位一定是 ,所以我们考虑如何把 其他位置的 消去。
依次消除 当前的最高位。注意对同一个数进行相加操作相当于左移一位,想要让 的最低位和最高位对齐,一共需要左移 的位数 位,设这个数为 。当 异或 后, 的最高位就被消掉了,设这个数为 。
记 , 的低位和 相同,高位相当于 的高位左移一位,中间不同的一位是因为 和 相加最高位的进位产生。之后我们将 左移一位,记作 。当我们将 与 异或后,得到 , 的最高位和 的最高位相同,其余位和 相同, 异或 就可以得到 的 ,记为 。我们用 每次消去 的一个 ,直到 只剩下最低位的 ,这样就得到了 的 ,就可以消掉 的最高位。一直消下去,直到 为 即可。
Code
#include<cstdio> #include<vector> #include<algorithm> #define LL long long using namespace std; LL x; struct Answer{ LL opt, x, y; }; vector<Answer> out; LL lowbit(LL x){ return x & (-x); } LL Change(LL x){ LL y = x, res = x >> 1/*每次需要左移 x 的二进制表示的位数 - 1位*/; while(res){ out.push_back((Answer){1, y, y}); y <<= 1; res >>= 1; } //这时 y 的最低次位已经和 x 的最高次位对齐了 LL z = x ^ y; //消去最高次位上的 1 out.push_back((Answer){2, x, y}); //printf("x = %lld y = %lld z = %lld\n", x, y, z); //然后去找 x 的 highbit,也就是把 y 消到只剩下一个 1 LL r = y + z; out.push_back((Answer){1, y, z}); LL s = y + y; out.push_back((Answer){1, y, y}); LL t = r ^ s; out.push_back((Answer){2, r, s}); LL u = t ^ x; out.push_back((Answer){2, t, x}); while(y != lowbit(y)){ if(y & u){ out.push_back((Answer){2, y, u}); y ^= u; } out.push_back((Answer){1, u, u}); u += u; //printf("y = %lld\n", y); } //printf("first x = %lld\n", x); out.push_back((Answer){2, x, y}); x ^= y; return x; } int main(){ scanf("%lld", &x); while(x != 1) x = Change(x); printf("%ld\n", out.size()); for(register int i = 0; i < out.size(); i++){ if(out[i].opt == 1) printf("%lld + %lld\n", out[i].x, out[i].y); else if(out[i].opt == 2) printf("%lld ^ %lld\n", out[i].x, out[i].y); } return 0; }
C.有趣的区间问题
CF1609F Interesting Sections
考虑枚举最大值和最小值的 popcount
,那么问题等价于,给定一个序列 ,其中有一些位置是关键位置,要求有多少个区间,满足其最大值和最小值都是关键位置。
枚举区间问题无非两个套路,扫描线和CDQ,这里用扫描线。枚举右端点 ,扫描到 时,我们记 表示 中最大值是否关键位置,以及 中最小值是否是关键位置,那么显然 的变化可以通过单调栈求出,那么对于一个右端点 和一个 popcount
,其对答案的贡献就是 的 的个数。注意到 的上界就是 ,因此这个可以通过区间最大值及其出现次数的线段树维护。
以及,这题卡常,用一下 fread
之类的差不多就能卡过去了。
Code
#include<cstdio> #include<vector> #include<algorithm> #define LL long long using namespace std; const int MAXN = 1e6 + 10, SIZE = 65; int n; LL ans; LL num[MAXN]; int cnt[MAXN], temp[SIZE]; int stk_max[MAXN], top_max, stk_min[MAXN], top_min; struct Change{ int pos, l, r, v; }; vector<Change> q[SIZE]; inline char gc(){ static char buf[MAXN], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXN, stdin), p1 == p2) ? EOF : *p1++; } inline LL read(){ LL x = 0, f = 1; char c = gc(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = gc(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = gc(); } return x * f; } inline LL lowbit(LL x){ return x & (-x); } inline int popcount(LL x){ int ans = 0; while(x){ ++ans; x -= lowbit(x); } return ans; } struct Segment_Tree{ struct Tree{ int l, r; int sum; int max; int lazy; }tr[MAXN << 2]; inline int lson(int rt){ return rt << 1; } inline int rson(int rt){ return rt << 1 | 1; } inline void Pushup(int rt){ tr[rt].sum = 0; tr[rt].max = max(tr[lson(rt)].max, tr[rson(rt)].max); if(tr[rt].max == tr[lson(rt)].max) tr[rt].sum += tr[lson(rt)].sum; if(tr[rt].max == tr[rson(rt)].max) tr[rt].sum += tr[rson(rt)].sum; } void Build(int rt, int l, int r){ tr[rt].l = l; tr[rt].r = r; tr[rt].max = 0; tr[rt].lazy = 0; tr[rt].sum = r - l + 1; if(l == r) return; int mid = (l + r) >> 1; Build(lson(rt), l, mid); Build(rson(rt), mid + 1, r); } inline void Pushdown(int rt){ if(tr[rt].lazy){ tr[lson(rt)].max += tr[rt].lazy; tr[rson(rt)].max += tr[rt].lazy; tr[lson(rt)].lazy += tr[rt].lazy; tr[rson(rt)].lazy += tr[rt].lazy; tr[rt].lazy = 0; } } void Update(int rt, int l, int r, int data){ if(tr[rt].l >= l && tr[rt].r <= r){ tr[rt].max += data; tr[rt].lazy += data; return; } Pushdown(rt); int mid = (tr[rt].l + tr[rt].r) >> 1; if(l <= mid) Update(lson(rt), l, r, data); if(r > mid) Update(rson(rt), l, r, data); Pushup(rt); } }S; int main(){ n = read(); for(register int i = 1; i <= n; ++i){ num[i] = read(); cnt[i] = popcount(num[i]); ++temp[cnt[i]]; } //for(register int i = 1; i <= n; i++) // printf("cnt[%d] = %d\n", i, cnt[i]); for(register int i = 1; i <= n; ++i){ while(top_min && num[stk_min[top_min]] > num[i]){ int p = stk_min[top_min--]; q[cnt[p]].push_back((Change){i, stk_min[top_min] + 1, p, -1}); } q[cnt[i]].push_back((Change){i, stk_min[top_min] + 1, i, 1}); stk_min[++top_min] = i; while(top_max && num[stk_max[top_max]] < num[i]){ int p = stk_max[top_max--]; q[cnt[p]].push_back((Change){i, stk_max[top_max] + 1, p, -1}); } q[cnt[i]].push_back((Change){i, stk_max[top_max] + 1, i, 1}); stk_max[++top_max] = i; } for(register int i = 0; i <= 64; ++i){ if(!temp[i]) continue; S.Build(1, 1, n); for(register int j = 1, sit = 0; j <= n; ++j){ while(sit < q[i].size() && q[i][sit].pos <= j){ S.Update(1, q[i][sit].l, q[i][sit].r, q[i][sit].v); ++sit; } if(S.tr[1].max == 2) ans += S.tr[1].sum; } } printf("%lld\n", ans); return 0; }
D.无聊的卡牌问题
CF1427F Boring Card Game
没搞懂呢,咕。
搞懂了,就是细节有点恶心,搞了一个上午。
Code
#include<queue> #include<cstdio> #include<algorithm> using namespace std; const int MAXN = 1210; int n, tot/*联通块的数量*/, num/*不存在依赖关系的为 0 的联通快的数量*/; int card[MAXN], belong[MAXN]/*这张牌归谁取*/, type[MAXN]/*联通快的类型*/, size[MAXN]/*联通快的大小*/; int deg[MAXN]/*联通块的入度*/, son[MAXN]/*联通块的儿子*/; int block[MAXN][4]; //每个联通块的成员 int stk[MAXN], top; bool vis[MAXN]; queue<int> q1, q2; inline int read(){ int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9'){ if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9'){ x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); } return x * f; } int main(){ n = read() * 3; for(register int i = 1; i <= n; i++){ card[i] = read(); belong[card[i]] = 1; //是先手 } n <<= 1; for(register int i = 1; i <= n; i++){ if(!top || type[stk[top]] != belong[i]){ stk[++top] = ++tot; type[tot] = belong[i]; ++size[tot]; block[tot][size[tot]] = i; } else{ int p = stk[top]; ++size[p]; block[p][size[p]] = i; if(size[p] == 3){ //弹栈 int q = stk[top - 1]; son[p] = q; ++deg[q]; --top; } } } for(register int i = 1; i <= tot; i++) //去找不存在依赖关系的为 0 的联通快 if(!deg[i] && !son[i] && !type[i]) ++num; for(register int i = 1; i <= tot; i++){ if(!deg[i] && type[i]) q1.push(i); if(!deg[i] && !type[i]) q2.push(i); } for(register int tim = 1; tim <= tot; tim++){ //第几次取 if(tim & 1){ //到先手取 while(!q1.empty()){ int t = q1.front(); q1.pop(); vis[t] = true; if(son[t]){ --deg[son[t]]; if(!deg[son[t]] && !vis[son[t]]){ q2.push(son[t]); if(!son[son[t]]) ++num; } } printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]); break; } } else{ while(!q2.empty()){ int t = q2.front(); q2.pop(); if(tim < tot && !son[t] && num == 1 && !q2.empty()){ q2.push(t); continue; } vis[t] = true; if(son[t]){ --deg[son[t]]; if(!deg[son[t]] && !vis[son[t]]) q1.push(son[t]); } if(!son[t]) --num; printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]); break; } } } return 0; }
以下为博客签名,与博文无关。
只要你们不停下来,那前面就一定有我。所以啊,不要停下来~
本文来自博客园,作者:TSTYFST,转载请注明原文链接:https://www.cnblogs.com/TSTYFST/p/16736153.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理