首页 > 其他分享 >CSP-S 模拟赛34

CSP-S 模拟赛34

时间:2024-10-04 21:51:09浏览次数:1  
标签:p2 p1 nx1 nx2 int sm2 34 CSP 模拟

CSP-S 模拟赛34

T1

考虑对原序列将 \(k\) 的左右分成两个序列。simple 的想法是分别从 \(1\) 开始跑前缀和,每一次总跑到下一个小于它的点,然后依次类推。发现这样做碰到序列最小值之后难以继续。

然而我们发现这样跑点的过程从前往后和从后往前是等价的。这样考虑的原因是发现这样的选数问题不具有方向性。于是时间复杂度 \(O(n)\)。

代码:

#include <bits/stdc++.h>
#define N 100005
#define int long long
using namespace std;
int T;
int n, k;
int a[N];
int a1[N], a2[N];
int ct1, ct2;
int sm1[N], sm2[N];
int nx1[N], nx2[N];
void sve() {
	memset(sm1, 0, sizeof sm1);
	memset(sm2, 0, sizeof sm2);
	memset(nx1, 0, sizeof nx1);
	memset(nx2, 0, sizeof nx2);
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	ct1 = ct2 = 1;
	a1[1] = a2[1] = 0;
	for (int i = k; i > 1; i--)
		a1[++ct1] = a[i];
	for (int i = k + 1; i <= n; i++)
		a2[++ct2] = a[i];
	for (int i = 1; i <= ct1; i++)
		sm1[i] = sm1[i - 1] + a1[i];
	for (int i = 1; i <= ct2; i++)
		sm2[i] = sm2[i - 1] + a2[i];
	int mn = sm1[1], nw = 1;
	for (int i = 2; i <= ct1; i++)
		if (sm1[i] <= mn) {
			nx1[nw] = i;
			nw = i;
			mn = sm1[i];
		}
	mn = sm1[ct1], nw = ct1;
	for (int i = ct1 - 1; i >= 1; i--)
		if (sm1[i] < mn) {
			nx1[nw] = i;
			nw = i;
			mn = sm1[i];
		}
	mn = sm2[1], nw = 1;
	for (int i = 2; i <= ct2; i++)
		if (sm2[i] <= mn) {
			nx2[nw] = i;
			nw = i;
			mn = sm2[i];
		}
	mn = sm2[ct2], nw = ct2;
	for (int i = ct2 - 1; i >= 1; i--)
		if (sm2[i] < mn) {
			nx2[nw] = i;
			nw = i;
			mn = sm2[i];
		}
	if (sm1[ct1] + sm2[ct2] > 0)
		return puts("No"), void();
	int p1 = 1, p2 = 1;
	while (nx1[p1] || nx2[p2]) {
		if (!nx1[p1]) {
			for (int j = p2 + 1; j <= nx2[p2]; j++)
				if (sm1[p1] + sm2[j] > 0) {
					puts("No");
					return;
				}
			p2 = nx2[p2];
			continue;
		}
		if (!nx2[p2]) {
			for (int j = p1 + 1; j <= nx1[p1]; j++)
				if (sm1[j] + sm2[p2] > 0) {
					puts("No");
					return;
				}
			p1 = nx2[p1];
			continue;
		}
		int fg = 0;
		for (int i = p1 + 1; i <= nx1[p1]; i++)
			if (sm1[i] + sm2[p2] > 0) {
				fg = 1;
				break;
			}
		if (!fg) {
			p1 = nx1[p1];
			continue;
		}
		for (int j = p2 + 1; j <= nx2[p2]; j++)
			if (sm1[p1] + sm2[j] > 0) {
				puts("No");
			return;
		}
		p2 = nx2[p2];
	}
	p1 = ct1, p2 = ct2;
	while (nx1[p1] || nx2[p2]) {
		if (!nx1[p1]) {
			for (int j = p2 - 1; j >= nx2[p2]; j--)
				if (sm1[p1] + sm2[j] > 0) {
					puts("No");
					return;
				}
			p2 = nx2[p2];
			continue;			
		}
		if (!nx2[p2]) {
			for (int j = p1 - 1; j >= nx1[p1]; j--)
				if (sm1[j] + sm2[p2] > 0) {
					puts("No");
					return;
				}
			p1 = nx2[p1];
			continue;			
		}
		int fg = 0;
		for (int i = p1 - 1; i >= nx1[p1]; i--)
			if (sm1[i] + sm2[p2] > 0) {
				fg = 1;
				break;
			}
		if (!fg) {
			p1 = nx1[p1];
			continue;
		}
		for (int j = p2 - 1; j >= nx2[p2]; j--)
			if (sm1[p1] + sm2[j] > 0) {
				puts("No");
			return;
		}
		p2 = nx2[p2];
	}
	puts("Yes");
}

signed main() {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
	cin >> T;
	for (int i = 1; i <= T; i++)
		sve();
	return 0;
}

T2

显然考虑 \(O(n^2)\) 的 dp。

朴素的 dp 定义是 \(dp_{i,j}\) 表示长度为 \(i\) 的序列,\(j\) 次消除的方案数。然而发现这样转移的复杂度难以接受,需要分别枚举左右区间的消除次数。

考虑某一个位置 \(x\) 的消除次数由什么决定。对于只有某一边有 \(>a_x\) 的,则这个位置的消除次数一定是 \(j-1\)。对于两边都有 \(>a_x\) 的,两边的消除次数有一个是 \(j-1\)。那么就考虑前缀和优化这个 dp,则定义 \(dp_{i,j,0/1}\) 表示长度为 \(i\) 的序列,至多 \(j\) 次消除,有一边 / 两边 \(>a_x\) 的方案数,那么 \(j\) 便由 \(j,j-1\) 转移而来。

时间复杂度大抵是 \(O(n^2\log^2n)\)。

标签:p2,p1,nx1,nx2,int,sm2,34,CSP,模拟
From: https://www.cnblogs.com/Rock-N-Roll/p/18447336

相关文章

  • CSP-S 模拟赛 32
    CSP-S模拟赛32rnk25,\(0+0+9+0=9\)。T1[CF1578K]KingdomofIslands还没改出来。T2[CF1578L]Labyrinth警钟嚼烂:改代码改干净。首先考虑如果从\(a\)走到\(b\),人最胖是多少。毫无疑问,这是一个最大生成树问题。在这个基础上考虑原问题,我们发现由于人会越来越胖,一定有边......
  • CSP-S模拟赛20241004
    A你考虑可以把这个数组当中的每个数表示成另一种形式:\(a_i=k_i\timesx+b\)(其中\(x\)是模数,\(b\)为余数)。对于求两个数是否对于某个余数同余,显然你要判断他们两个的差,即\(a_i-a_j\),那么我们用上面那种形式表示其实就是\(a_i-a_j=(k_i-k_j)\timesx\),所以你要判断整个数......
  • CSP-JS多省分数线分析!复赛如何准备?(附复赛流程视频)
    截止目前已经有多个省份CSP-JS的分数线已经出了,很多省份比去年提升了不少,像河南等地都提升了20多分,不过也有一些省份,天津比去年提升分数却不是很多。还有很多省份分数线没出,各位家长想要预估是否能够晋级的,以下是已出分数线省份表格统计:目前已出分数线省份省份入门组......
  • [CSP-J 2023] 小苹果(apple)-----------用数组
    1#include<iostream>2usingnamespacestd;3intmain(){4intn,m;5cin>>n>>m;6intg=n,t=0,li,s[n+1],b;7for(inti=1;i<=n;i++){8s[i]=i;9}10while(g){11t+=1,b=0,li=0,g-=(g+......
  • CSP小苹果详细解法
    #include<bits/stdc++.h>usingnamespacestd;intmain(){intn,ant=0,t,j;cin>>n;cout<<"小苞的桌上一共放了"<<n<<"个苹果。"<<endl;inta[n+5],b[n+5];for(inti=1;i<=n;i++){a[i......
  • 信息学奥赛复赛复习11-CSP-J2020-04方格取数-动态规划、斐波那契数列、最优子结构、重
    PDF文档公众号回复关键字:202410041P7074[CSP-J2020]方格取数[题目描述]设有n×m的方格图,每个方格中都有一个整数。现有一只小熊,想从图的左上角走到右下角,每一步只能向上、向下或向右走一格,并且不能重复经过已经走过的方格,也不能走出边界。小熊会取走所有经过的方格中......
  • 【C++】 string类的模拟实现
    目录string类各函数接口总览构造函数拷贝构造函数赋值运行符重载函数析构函数迭代器相关函数beginend容量和大小相关的函数sizecapacityresizereserveempty修改字符串相关函数push_backappendoperator+=inserteraseclearswapc_str访问字符串相关函数o......
  • 冲刺CSP联训模拟2
    冲刺CSP联训模拟2过T2了,赢了T3T4暴力没写满,输了A挤压我是唐诗老哥一个半小时才过T1发现要求的是\(E(s^2)\),因为有个异或,所以直接考虑拆贡献到每一位\[E(s^2)=E(\sums_i\sums_j)=\sumE(s_is_j)\]所以直接考虑后面那个咋做,就是\(i,j\)位同时是一的时候贡献\(2^......
  • 20241003 模拟赛
    这场...打得还行吧。(至少没有爆零A.旋律的总数难度:橙签到题。只要第一个都选\(1\),就能保证不同。答案为\(m^{n-1}\)。#include<bits/stdc++.h>#definelllonglong#definemod1000000007usingnamespacestd;intT;lln,m;llquickpow(lla,llb){llre......
  • 10.2 代码源 2024 CSP-S 模拟赛 Day 7
    省流:\(55+5+0+10=70\)简称:唐诗T1第一眼发现在二进制下加法不能进位,然后码了个DP就放那了……DP代码:intS=(1<<14)|1;fd(i,0,r[1])f[1][i]=1;fd(i,2,n){fd(j,0,S){f[i][j]=f[i-1][j];for(ints=j;s;s=(s-1)&j){(f......