2026CSP-S模拟5

卸载铅棉

T1 注意到了答案小于等于 2 的性质,但是没有注意到数据范围有 0 ,怒挂 21pts ; T2 注意到没有注意到任何转化/性质,于是不会,赛后发现自己唐完了; T3 一眼疑似数据结构维护,发现不会,开始思考发现一个比暴力多个老哥的做法(¿),之后想到平衡树发现不很会写于是打的暴力; T4 发现小学没学好不会数三角形,大份读入我已急哭。

推歌:《逃避法律摇滚》(题图为歌曲封面)。


T1 花间叔祖

\(D\) 得到了一个叔祖,他想进行花间。

原:ARC148A。

因为模数大于等于 2 ,所以显然答案小于等于 2 ,考虑每个元素 \(mod\) 某个数模数一样的情况,发现对原数组进行差分就相当于这个差分能被那个数整除,于是差分之后求 \(gcd\) ,如果不等于 1 答案就是 1 ,否则是 2 。

赛时为什么挂分了呢?因为当原序列的所有数均为 0 的时候 \(gcd\) 是 0 ,但是我的判断条件写成了 g>1

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int _ = 200010;
int n, a[_], c[_], g;
signed main(){
	ios :: sync_with_stdio(0), cin. tie(0), cout. tie(0);
	cin >> n;
	cin >> a[1];
	for(int i = 2; i <= n; i ++){
		cin >> a[i];
	}
	sort(a + 1, a + n + 1);
	n = unique(a + 1, a + n + 1) - a - 1;
	for(int i = 2; i <= n; i ++){
		g = __gcd(a[i] - a[1], g);
	}
	if(g != 1 || n == 1){
		cout << 1;
		return 0;
	}
	cout << 2;
	return 0;
}

T2 合并r

\(Delov\) 曾名 lxhcr ,真名 lx , 如你所见,他好吃 \(r\)

原:ARC107D。
\(\frac{1}{2^i}\) 这个东西奇丑陋无比,但如果我们可以凑出来 \(j\) ,那么将选出来的所有数都除以二,就可以把 \(\frac{j}{2}\) 凑出来,而题目中我们可以选择的数正是 \(1/2/2/2...\)

于是就转化出来两种操作:新加上一个 \(1\) ,从 \(j*2\) 继承方案数。

于是可以出状态转移方程$$dp_{i,j} = dp_{1 - 1, j - 1} + dp_{i, j * 2}$$

代码奇好写无比。

Code

#include <bits/stdc++.h>
using namespace std;
const int _ = 5010, mod = 998244353;
int n, k, dp[_][10086];
int main(){
	ios :: sync_with_stdio(0), cin. tie(0), cout. tie(0);
	cin >> n >> k;
	if(n == k){
		cout << 1;
		return 0;
	}
	dp[0][0] = 1;
	for(int i = 1; i <= n; i ++){
		for(int j = n; j; j --){
			dp[i][j] = (dp[i - 1][j - 1] + dp[i][j * 2]) % mod;
		}
	}
	cout << dp[n][k];
	return 0;
}

T3 回收波特

Delov 不和 npynpy 们玩了,这回他和他的波特们一起玩。
原:ARC149D。

题解说官解写得很棒,于是我也觉得官解很棒,于是我也放官解。

然后这里不是官解。

发现如果暴力处理 \(O(n^2)\) 显然会 T ,数据结构维护的话,因为原点左右两边处理方式刚好相反,不好直接做,用 fhq 搞每次分裂合并可能会出现 \(m\) 次操作每次操作 \(n\) 个数都移到原点另一边最终复杂度变成比暴力还多老哥的神秘状物,非常难受。

但是发现如果一次移动之后一些 bot 移动到了了相同位置,那么可以直接合并,因为以后的运动它们一定始终都在同样的位置了。

于是把平衡树按照权值分裂开更新之后再将两棵树的有交的部分用并查集合并,并删掉相同位置的其它元素,就可以保证复杂度了(也许吧我不会分析qwq)。

具体的超级拼装看代码细节。

Code

#include <bits/stdc++.h>
using namespace std;
mt19937 rain(20100708);
const int _ = 300010;
int n, m, x, d, fa[_], cnt, rt, ans[_];
bool ok[_];
inline int find(int x){
	return (fa[x] == x ? x : fa[x] = find(fa[x]));
}
inline void hebing(int x, int y){
	x = find(x), y = find(y);
	if(x == y){
		return ;
	}
	fa[x] = y;
	return ;
}
#define ls(x) tree[(x)]. ls
#define rs(x) tree[(x)]. rs
#define r(x) tree[(x)]. rnd
#define l(x) tree[(x)]. lz
#define v(x) tree[(x)]. v
#define p(x) tree[(x)]. p
struct hhh{
	int ls, rs, v, p, lz, rnd;
}tree[_];
inline int nw(int v, int p){
	tree[++ cnt] = {0, 0, v, p, 0, rain()};
	return cnt;
}
inline void pushdown(int root){
	if(l(root)){
		if(ls(root)){
			v(ls(root)) += l(root), l(ls(root)) += l(root);
		}
		if(rs(root)){
			v(rs(root)) += l(root), l(rs(root)) += l(root);
		}
		l(root) = 0;
	}
	return ;
}
inline void split(int root, int k, int & x, int & y){
	if(! root){
		x = y = 0;
		return ;
	}
	pushdown(root);
	if(v(root) <= k){
		x = root;
		split(rs(root), k, rs(root), y);
	}
	else{
		y = root;
		split(ls(root), k, x, ls(root));
	}
	return ;
}
inline int merge(int x, int y){//普通拼装。
	if(! x || ! y){
		return x | y;
	}
	if(r(x) < r(y)){
		pushdown(x);
		rs(x) = merge(rs(x), y);
		return x;
	}
	else{
		pushdown(y);
		ls(y) = merge(x, ls(y));
		return y;
	}
}
inline void add(int v, int p){
	int x, y, z;
	z = nw(v, p);
	split(rt, v, x, y);
	rt = merge(merge(x, z), y);
	return ;
}
inline void changef(int root, int v){
	if(! root){
		return ;
	}
	hebing(p(root), v);
	changef(ls(root), v);
	changef(rs(root), v);
	return ;
}
inline int supper_merge(int x, int y){
	if(! x || ! y){
		return x | y;
	}
	if(r(x) > r(y)){//把y并到x上。
		swap(x, y);
	}
	pushdown(x);
	int a, b, c;
	split(y, v(x), a, b);
	split(a, v(x) - 1, a, c);
	changef(c, p(x));//把和x位置一样的扔掉用并查集维护。
	ls(x) = supper_merge(ls(x), a);
	rs(x) = supper_merge(rs(x), b);
	return x;
}
inline void suan(int root, int v){
	if(! root){
		return ;
	}
	pushdown(root);
	if(v){
		ans[p(root)] = v;
		ok[p(root)] = 1;
	}
	else{//走不到原点。
		ans[p(root)] = v(root);
	}
	suan(ls(root), v);
	suan(rs(root), v);
	return ;
}
inline void walk(int v, int nw){
	int x, y, z;
	split(rt, 0, x, y);
	if(x){
		l(x) += v, v(x) += v;
	}
	if(y){
		l(y) -= v, v(y) -= v;
	}
	rt = supper_merge(x, y);//把有交的两棵树超级拼装,并查集合并有交的部分。
	split(rt, 0, x, y);//把0分裂出来。
	split(x, - 1, x, z);
	suan(z, nw);//统计答案。
	rt = merge(x, y);
	return ;
}
int main(){
	ios :: sync_with_stdio(0), cin. tie(0), cout. tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; i ++){
		cin >> x;
		fa[i] = i;
		add(x, i);
	}
	for(int i = 1; i <= m; i ++){
		cin >> d;
		walk(d, i);
	}
	suan(rt, 0);
	for(int i = 1; i <= n; i ++){
		fa[i] = find(i);
		if(ok[fa[i]]){
			cout << "Yes ";
		}
		else{
			cout << "No ";
		}
		cout << ans[fa[i]] << '\n';
	}
	return 0;
}

T4 斗篷

本场真正的饺子醋来辣!超大份读入,你值得拥有!

这个是学长自己出的。
对于每个点,我们钦定它为正放的三角形的右下角。维护出这个点向左上方、右上方、左方延伸出来的最大距离,直接上扫描线。

读入很大份。


Code

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x & (- x))
const int _ = 10086;
int pre[_], r, c, lft[2][_], rgh[2][_], tree[_], tot;
long long ans;
bool o;
char h;
string s[_ << 1];
inline void add(int x, int y){
	for(; x; x -= lowbit(x)){
		tree[x] += y;
	}
	return ;
}
struct hhh{
	int p, l;
}q[_ << 1];
inline bool bbb(hhh x, hhh y){
	return x. p < y. p;
}
inline int query(int x){
	int as = 0;
	for(; x <= r + c; x += lowbit(x)){
		as += tree[x];
	}
	return as;
}
inline void suan(){
	o = 0;
	for(int i = 1; i <= r; i ++){
		o ^= 1;
		memset(tree, 0, sizeof(tree));
		tot = 0;
		int st = (s[(i << 1) - 1][1] == ' ') + 1;
		for(int j = st, num = 1; j <= c; j += 2, num ++){
			int x = (i << 1) - 1, y = (j << 1) - 1;
			if(i != 1 && j != 1 && s[x - 1][y - 1] == 0x5c){
				lft[o][j] = lft[o ^ 1][j - 1] + 1;
			}
			else{
				lft[o][j] = 0;
			}
			if(i != 1 && j != c && s[x - 1][y + 1] == '/'){
				rgh[o][j] = rgh[o ^ 1][j + 1] + 1;
			}
			else{
				rgh[o][j] = 0;
			}
			if(j != st && s[x][y - 2] == '-'){
				pre[j] = pre[j - 2];
			}
			else{
				pre[j] = j;
			}
			q[++ tot] = {max(pre[j], j - lft[o][j] - lft[o][j]), num};
			ans += query(num);
			add(num + rgh[o][j], 1);
		}
		sort(q + 1, q + tot + 1, bbb);
		memset(tree, 0, sizeof(tree));
		int nw = 1;
		for(int j = st, num = 1; j <= c; j += 2, num ++){
			while(nw <= tot && q[nw]. p == j){
				ans -= query(q[nw ++]. l);
			}
			add(num + rgh[o][j], 1);
		}
	}
	return ;
}
int main(){
//	ios ::sync_with_stdio(0), cin. tie(0), cout. tie(0);
	cin >> r >> c;
	getchar(), h = getchar();
	while(h != ' ' && h != 'x' && h != 0x5c && h != '/' && h != '-'){
		h = getchar();
	}
	for(int i = 1; i <= r * 2 - 1; i ++){
		s[i] = ' ';
		while(h == ' ' || h == 'x' || h == 0x5c || h == '/' || h == '-'){
			if(h == ' '){
				s[i] += ' ';
			}
			else{
				s[i] += h;
			}
			h = getchar();
		}
		if(i < r * 2 - 1){
			while(h != ' ' && h != 'x' && h != 0x5c && h != '/' && h != '-'){
				h = getchar();
			}	
		}
	}
	suan();
	for(int i = 1; i < r; i ++){
		for(int j = 1; j <= c * 2 - 1; j ++){
			swap(s[i][j], s[(r << 1) - i][j]);
		}
	}
	for(int i = 1; i <= (r << 1) - 1; i ++){
		for(int j = 1; j <= (c << 1) - 1; j ++){
			if(s[i][j] == '/'){
				s[i][j] = 0x5c;
			}
			else if(s[i][j] == 0x5c){
				s[i][j] = '/';
			}
		}
	}
    suan();
    cout << ans;
	return 0;
}

The End.

《脱法》其实挺好听的就是PV难看。
Everybody
去依赖于现实逃避吧 去依赖吧
去变成丧家犬狂吠吧 去狂吠吧
把理想形象舍弃掉吧 舍弃掉吧
那便是 逃避法律摇滚的 礼节啊
向着最底边沦落下去吧 沦落下去吧
堕落成为不适合社会的人吧 堕落吧
不停地把乌龙球踢进去吧 踢进去吧
那便是 逃避法律摇滚的 礼节啊
没有必要讨人喜欢 没有必要
引发起化学反应吧 引发起吧
自尊心就随手丢掉吧 丢掉吧
那便是 逃避法律摇滚的 礼节啊
不知道什么体面不体面 不知道
音量已经无法降低 无法降低
持续不断地摄取yaopin吧 摄取吧
那便是 逃避法律摇滚的 礼节啊
一年到头都得意忘形起来吧~
posted @ 2026-07-06 20:32  养鸡大户肝硬化  阅读(20)  评论(0)    收藏  举报