『模拟赛』CSP-S模拟5
Rank
一般
A. 光
胱!
妈的传奇题目控我两个小时想
其实带下取整的四个四元一次方程根本解不了,考虑到这个题给的范围只有
学的 K8He 的带
想卡最优解来着,所以提前跑了一遍把剩余局面的所有消耗跑出来赋给数组了,结果还是比不过小常数选手们,拜谢!
注释掉的是跑剩余局面的代码,有需要的自行打开。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x) = (y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x) = (y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 2e5 + 5 , M = 6000;
const int mod = 998244353;
int a, b, c, d;
int ans = 1e9;
int res[4][4][4][4]={0,1,2,3,1,2,2,3,2,2,3,4,3,3,4,4,1,2,2,3,2,2,2,3,3,3,3,4,4,4,4,4,2,2,
3,4,3,3,3,4,4,4,4,4,5,5,5,5,3,3,4,4,4,4,4,4,5,5,5,5,6,6,6,6,1,2,3,4,2,3,3,4,2,2,3,4,3,3,
4,4,2,3,3,4,3,3,3,4,3,3,4,4,4,4,4,4,2,2,3,4,3,3,4,4,4,4,4,4,5,5,5,5,3,3,4,4,4,4,4,4,5,5,
5,5,6,6,6,6,2,3,4,5,2,3,4,5,3,3,4,5,4,4,5,5,2,3,4,5,2,3,4,5,3,4,4,5,4,4,5,5,3,3,4,5,3,4,
4,5,4,4,4,5,5,5,5,6,4,4,5,5,4,4,5,5,5,5,5,6,6,6,6,6,3,4,5,6,3,4,5,6,4,4,5,6,4,4,5,6,3,4,
5,6,3,4,5,6,4,4,5,6,4,4,5,6,4,4,5,6,4,4,5,6,5,5,5,6,5,5,6,6,4,4,5,6,4,4,5,6,5,5,6,6,6,6,6,6};
priority_queue<pii, vector<pii >, less<pii > > q;
namespace Wisadel
{
void Wfang(pii x, pii y)
{
if(x.se + y.se == 5) q.push({y.fi - 1, y.se});
else q.push({y.fi - 2, y.se});
}
short main()
{
freopen("light.in", "r", stdin) , freopen("light.out", "w", stdout);
a = qr, b = qr, c = qr, d = qr;
fo(i1, 0, 3) fo(i2, 0, 3) fo(i3, 0, 3) fo(i4, 0, 3)
{
int A = a - i1, B = b - i2, C = c - i3, D = d - i4;
q.push({A, 1}), q.push({B, 2}), q.push({C, 3}), q.push({D, 4});
int tim = 0, cs;
int ba, bb, bc, bd;
while(q.top().fi >= 4)
{
pii aa = q.top(); q.pop();
pii bb = q.top(); q.pop();
pii cc = q.top(); q.pop();
pii dd = q.top(); q.pop();
q.push({aa.fi - 4, aa.se});
Wfang(aa, bb), Wfang(aa, cc), Wfang(aa, dd);
tim++;
}
A = max(q.top().fi, 0), ba = q.top().se, q.pop(), B = max(q.top().fi, 0), bb = q.top().se, q.pop(),
C = max(q.top().fi, 0), bc = q.top().se, q.pop(), D = max(q.top().fi, 0), bd = q.top().se, q.pop();
cs = A + B + C + D;
if(ba + bd != 5) swap(B, D), swap(bb, bd);
if(ba + bd != 5) swap(C, D), swap(bc, bd);
fo(i, 0, A) fo(j, 0, B) fo(k, 0, C)
{
int l = -1;
if(A - i - (j / 2) - (k / 2) >= 0) l = max(l, (A - i - (j / 2) - (k / 2)) * 4);
if(B - j - (i / 2) - (k / 4) >= 0) l = max(l, (B - j - (i / 2) - (k / 4)) * 2);
if(C - k - (i / 2) - (j / 4) >= 0) l = max(l, (C - k - (i / 2) - (j / 4)) * 2);
if(D - (i / 4) - (j / 2) - (k / 2) >= 0) l = max(l, D - (i / 4) - (j / 2) - (k / 2));
if(l != -1) cs = min(cs, i + j + k + l);
}
// fo(i, 0, i1) fo(j, 0, i2) fo(k, 0, i3)
// {
// int l = -1;
// if(i1 - i - (j / 2) - (k / 2) >= 0) l = max(l, (i1 - i - (j / 2) - (k / 2)) * 4);
// if(i2 - j - (i / 2) - (k / 4) >= 0) l = max(l, (i2 - j - (i / 2) - (k / 4)) * 2);
// if(i3 - k - (i / 2) - (j / 4) >= 0) l = max(l, (i3 - k - (i / 2) - (j / 4)) * 2);
// if(i4 - (i / 4) - (j / 2) - (k / 2) >= 0) l = max(l, i4 - (i / 4) - (j / 2) - (k / 2));
// if(l != -1) res = min(res, i + j + k + l);
// }
// cout<<res<<',';
ans = min(ans, res[i1][i2][i3][i4] + tim * 4 + cs);
}
// cout<<endl;
printf("%d\n", ans);
return Ratio;
}
}
int main(){return Wisadel::main();}
B. 爬
感觉最简单的一道,赛时 20min 就做出来了,不过因为 T1 耽误太久导致没细想复杂度,暴力找每一个点的所有贡献好像无论时间空间都是
其实关键点在于想到每个点的贡献是独立的,只与它和它的子节点有关。一共只有
然后考虑怎么求每个点的贡献和。一种优化的思路是二进制拆分。对于二进制下数的每一位,只有出现的次数为奇异或起来才会有贡献,因此我们可以将每个数拆成二进制,在统计总贡献时先求出一点和它的子节点每一位上为 1 的数的个数,再逐位去求,那么有贡献的方案应该是
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x) = (y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x) = (y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 1e5 + 5;
const int mod = 1e9 + 7;
int n;
int fa[N], a[N][31], num[N][31];
int hh[N], to[N], ne[N], cnt;
ll Ans;
namespace Wisadel
{
void Wadd(int u, int v)
{
to[++cnt] = v;
ne[cnt] = hh[u];
hh[u] = cnt;
}
ll Wqp(ll x, int y)
{
ll res = 1;
while(y)
{
if(y & 1) res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
void Wdfs(int u, int fa)
{
int tot = (u != 1);
for(int i = hh[u]; i != -1; i = ne[i])
{
int v = to[i];
if(v == fa) continue;
tot++;
Wdfs(v, u);
fo(j, 0, 30) num[u][j] += a[v][j];
}
ll res = 0;
if(u != 1)
{
fo(i, 0, 30)
{
if(!num[u][i]) continue;
res = (res + (1ll << i) * (Wqp(2, num[u][i] - 1) * Wqp(2, tot - num[u][i]) % mod - num[u][i] + mod) % mod) % mod;
}
}
else
{
fo(i, 0, 30)
{
if(a[u][i])
{
if(!num[u][i])
res = (res + (1ll << i) * (Wqp(2, tot) - 1) % mod) % mod;
else
res = (res + (1ll << i) * (Wqp(2, num[u][i] - 1) * Wqp(2, tot - num[u][i]) % mod - 1 + mod) % mod) % mod;
}
else
{
if(!num[u][i]) continue;
res = (res + (1ll << i) * Wqp(2, num[u][i] - 1) % mod * Wqp(2, tot - num[u][i]) % mod) %mod;
}
}
}
Ans = (Ans + res * Wqp(2, n - 1 - tot) % mod) % mod;
}
short main()
{
freopen("climb.in", "r", stdin) , freopen("climb.out", "w", stdout);
n = qr;
memset(hh, -1, sizeof hh);
fo(i, 1, n)
{
int bb = qr, ttt = 0;
while(bb) a[i][ttt] = bb & 1, bb >>= 1, ttt++;
if(i != 1) fo(j, 0, ttt) num[i][j] = a[i][j];
}
fo(i, 2, n) fa[i] = qr, Wadd(fa[i], i);
Wdfs(1, 0);
printf("%lld\n",Ans);
return Ratio;
}
}
signed main(){return Wisadel::main();}
C. 字符串
贪心 唐氏大分讨。
感觉做到这如果有时间静下心来想应该很好得出结论:只有
我们枚举交替串的数量,规定
头脑冷静,理清思路最重要。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x) = (y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x) = (y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 1e5 + 5;
const int mod = 1e9 + 7;
int n, m, a, b, c;
int ans;
namespace Wisadel
{
short main()
{
freopen("string.in", "r", stdin) , freopen("string.out", "w", stdout);
int T = qr;
while(T--)
{
n = qr, m = qr, a = qr, b = qr, c = qr;
int sum = 0; ans = -1e9;
fo(i, 0, m / c)
{
sum = i * 2 + (c - 1) / b * i;
int _n = n - i, _m = m - i * c;
if(_n < 0 || _m < 0) break;
if(_m)
{
_m--, sum++;
if(_m)
{
int w = min(i, _m / (b - (c - 1) % b));
sum += w + (_m - w * (b - (c - 1) % b)) / b;
}
}
if(_n)
{
_n--,sum++;
if(_n) sum += _n / a;
}
ans = max(ans, sum);
}
printf("%d\n", ans);
}
return Ratio;
}
}
int main(){return Wisadel::main();}
D. 奇怪的函数
赛时没时间了,5pts 暴力都没打完。
稍微手模一下就能发现这
容易想到线段树,以操作为维护对象,维护每个区间有实际意义值域对应的定义域范围,所有 1 操作的总和。由于可能存在上述定义域范围不存在的情况,而此时该函数实质上变为了一个定函数,即只有一个值,所以我们还需要维护这个值。
考虑两个问题:首先是边界问题。比较好得出取 min 操作是划定定义域的上界,取 max 划定下界。其他情况我们将范围赋成极大区间即可。定值由于是定值?无论取啥都一样。
第二是 pushup,考虑如何合并两个分段函数。1 操作的总和不用说,直接加就行。定义域,我们要取左区间的定义域在右区间上仍有意义的区间,思考一下,从左到右之间做了左区间的加操作,那么若一个数在定义域内,一定有
其中
然后就做完了,比一般的线段树甚至好写。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x) = (y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x) = (y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch = getchar();lx x = 0 , f = 1;
for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int , int>
#define fi first
#define se second
const int Ratio = 0;
const int N = 3e5 + 5;
const int mod = 1e9 + 7;
int n, m;
int op[N], v[N];
ll L[N << 2], R[N << 2], sum[N << 2], ans[N << 2];
namespace Wisadel
{
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define mid ((l + r) >> 1)
void Wpushup(int rt)
{
L[rt] = max(L[ls], L[rs] - sum[ls]), R[rt] = min(R[ls], R[rs] - sum[ls]);
sum[rt] = sum[ls] + sum[rs];
if(L[rs] > R[rs]) ans[rt] = ans[rs];
else if(ans[ls] < L[rs]) ans[rt] = L[rs] + sum[rs];
else if(ans[ls] > R[rs]) ans[rt] = R[rs] + sum[rs];
else ans[rt] = ans[ls] + sum[rs];
}
void Wbuild(int rt, int l, int r)
{
L[rt] = -1e9, R[rt] = 1e9;
if(l == r)
{
if(op[l] == 1) sum[rt] = v[l], ans[rt] = v[l];
else if(op[l] == 2) R[rt] = v[l], ans[rt] = 0;
else L[rt] = v[l], ans[rt] = v[l];
return ;
}
Wbuild(ls, l, mid), Wbuild(rs, mid + 1, r);
Wpushup(rt);
}
void Wupd(int rt, int l, int r, int x, int k, int opp)
{
if(l == r)
{
if(opp == 1) L[rt] = -1e9, R[rt] = 1e9, ans[rt] = sum[rt] = k;
else if(opp == 2) L[rt] = -1e9, R[rt] = k, ans[rt] = sum[rt] = 0;
else ans[rt] = L[rt] = k, R[rt] = 1e9, sum[rt] = 0;
return ;
}
if(x <= mid) Wupd(ls, l, mid, x, k, opp);
else Wupd(rs, mid + 1, r, x, k, opp);
Wpushup(rt);
}
ll Wq(int x)
{
if(L[1] > R[1]) return ans[1];
if(x < L[1]) return 1ll * L[1] + sum[1];
else if(x > R[1]) return 1ll * R[1] + sum[1];
else return x + sum[1];
}
short main()
{
freopen("function.in", "r", stdin) , freopen("function.out", "w", stdout);
n = qr;
fo(i, 1, n) op[i] = qr, v[i] = qr;
Wbuild(1, 1, n);
m = qr;
fo(i, 1, m)
{
int opp = qr, pos = qr, vv;
if(opp == 4) printf("%lld\n", Wq(pos));
else vv = qr, Wupd(1, 1, n, pos, vv, opp);
}
return Ratio;
}
}
int main(){return Wisadel::main();}
末
这场策略依托。
感觉挺玄学的,就这场没有通读题面,就这场四道可做题。赛时光觉着 T1 大水,切不了寄寄,然后被控了 2h,本来 T3 会 50pts,T4 会 20pts,结果没做到,T2 的唐错也没拍出来,如果没写完算挂的话,这场挂了得有快 100pts 了。
还有,谁家好人打模拟赛的时候练马蜂啊啊啊!昨天打一天超级线段树因为写紧了调试不方便看所以就加了空格,然后看啥都想加空格,T1 有一半时间推式子,另一半全驯服代码了。
吃一堑长一智,以后得注意考场策略了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探