当前位置: 首页> 健康> 美食 > 河北招投标信息网官网_武汉建设学校_百度推广客服工作怎么样_常州百度seo排名

河北招投标信息网官网_武汉建设学校_百度推广客服工作怎么样_常州百度seo排名

时间:2025/7/10 17:13:50来源:https://blog.csdn.net/ZXTpower/article/details/146715563 浏览次数:0次
河北招投标信息网官网_武汉建设学校_百度推广客服工作怎么样_常州百度seo排名

[CSP-J2022 山东] 植树节

题目背景

受疫情影响,山东省取消了 CSP-J 2022 认证活动,并于次年三月重新命题,在省内补办比赛。

题目描述

植树节快要到了,学校要组织志愿者去给树苗浇水。

有一排树苗,编号依次是 0 , 1 , 2 , … 0,1,2,\dots 0,1,2,

现有 n n n 个志愿者去给树苗浇水,第 i i i 个志愿者选定了一个区间 [ a i , b i ] \left[a_{i},b_{i}\right] [ai,bi] ,表示第 i i i 个志愿者将 [ a i , b i ] \left[a_{i},b_{i}\right] [ai,bi] 这一区间内的每一棵树都浇一次水。

如某个志愿者选择的浇水区间为 [ 4 , 9 ] \left[4,9\right] [4,9] ,表示他将给编号为 4 , 5 , 6 , 7 , 8 , 9 4,5,6,7,8,9 4,5,6,7,8,9 的树各浇水一次。

当所有的志愿者完成各自所选区间的浇水后,可能有些树苗被不同的志愿者浇水多次,也可能有的树苗一次也没被浇过水。

请你求出浇水最多的树苗被浇了多少次。

输入格式

1 1 1 行,一个整数 n n n ,表示志愿者的人数。

2 2 2 行到第 n + 1 n+1 n+1 行,每行两个整数 $a_{i},b_{i} ( ( i=0,1,2,\dots n-1$),表示志愿者 i i i 选择的浇水区间。

输出格式

输出 1 1 1 行, 1 1 1 个整数,表示浇水最多的树苗被浇水的次数。

输入输出样例 #1

输入 #1

4
0 2
2 4
1 4
6 7

输出 #1

3

输入输出样例 #2

输入 #2

4
1000000 1000000
1000000 1000000
0 1000000
1 1000000

输出 #2

4

说明/提示

数据范围

  • 对于所有的数据: n < 1 0 5 n<10^{5} n<105 0 ≤ a i ≤ b i ≤ 1 0 6 0\le a_{i}\le b_{i}\le 10^{6} 0aibi106
测试点编号 a i ≤ a_{i}\le ai b i ≤ b_{i}\le bi n i ≤ n_{i}\le ni特殊性质
1 , 2 , 3 1,2,3 1,2,3 1 0 3 10^{3} 103 1 0 3 10^{3} 103 1 0 3 10^{3} 103
4 , 5 , 6 , 7 4,5,6,7 4,5,6,7 1 0 6 10^{6} 106 1 0 6 10^{6} 106 1 0 5 10^{5} 105
8 8 8 1 0 6 10^{6} 106 1 0 6 10^{6} 106 1 0 5 10^{5} 105 a i = b i a_{i}=b_{i} ai=bi
9 9 9 1 0 6 10^{6} 106 1 0 6 10^{6} 106 1 0 5 10^{5} 105 a i = 1 , b i = 1 0 3 a_{i}=1,b_{i}=10^{3} ai=1,bi=103
10 10 10 1 0 6 10^{6} 106 1 0 6 10^{6} 106 1 0 5 10^{5} 105

解题思路

很模板的一道差分题目,操作是区间元素全部加上一个数,在最后单点查询,竟然不需要开 long long,不知道怎么想的出这个题。

#include <bits/stdc++.h>using namespace std;const int maxt = 1e6 + 5;
int n, d[maxt]; // d 表示差分数组,一开始都是 0int main()
{cin >> n;for (int i = 1; i <= n; i++){int l, r;cin >> l >> r; d[l]++; // 差分操作d[r + 1]--;}int ans = 0, ti = 0; // ti 表示当前这个地方被浇了多少次水,ans 是最大次数for (int i = 0; i <= 1000000; i++){ti += d[i];ans = max(ans, ti);}cout << ans;return 0;
}

[CSP-J2022 山东] 宴会

题目背景

受疫情影响,山东省取消了 CSP-J 2022 认证活动,并于次年三月重新命题,在省内补办比赛。

题目描述

今人不见古时月,今月曾经照古人。梦回长安,大唐风华,十里长安花,一日看尽。

唐长安城是当时世界上规模最大、建筑最宏伟、规划布局最为规范化的一座都城。其营建制度规划布局的特点是规模空前、创设皇城、三城层环、六坡利用、布局对称、街衢宽阔、坊
里齐整、形制划一、渠水纵横、绿荫蔽城、郊环祀坛。而所谓的十里长安街,位于长安城的中轴线上,即唐长安城的朱雀大街,又称承天门大街。唐朝官员们住在各个“坊”里,上朝下朝都需要通过朱雀大街。

为了保持各大家族的联系和友谊,各官员往往会每月办一次宴会。为了方便描述,我们把朱雀大街看成一个数轴,各官员所居住的“坊”缩略为数轴上的一个坐标点。大家决定选一处地点(该地点是数轴上的某一个点,不一定坐标点)办宴会。由于唐朝宵禁严格,大家又都希望交流时间尽可能长,因此想要使宴会开始时间尽可能早。又因为大唐注重礼仪,因此,参加宴会的官员会花一定时间盛装打扮过后才前往宴会地点(不一定是坐标点)。

更具体地,一条纵向的街道上(相当于一维坐标)有 n n n 个人居住,其中第 i i i 个人居住在 x i x_{i} xi (非负整数)位置(坐标点)上。每月他们会选择在 x 0 x_{0} x0(数轴上的某一个点,不一定坐标点)出举办宴会。

已知第 i i i 个人从 x i x_{i} xi 出发前往宴会地点 x 0 x_{0} x0 处需要花费 ∣ x i − x 0 ∣ \left|x_{i}-x_{0}\right| xix0 的时间,另外,他还需要花费 t i t_{i} ti 的时间进行打扮,换言之,他共需要花费 ∣ x i − x 0 ∣ + t i \left|x_{i}-x_{0}\right|+t_{i} xix0+ti 的时间到达宴会举办处。

假设初始时刻为 0 0 0。这 n n n 个人开始打扮和出发前往宴会处,他们想要使得宴会的开始时间尽可能早,于是向你求助,请你帮助他们确定好最优的宴会举办地点 x 0 x_{0} x0

注: ∣ x i − x 0 ∣ \left|x_{i}-x_{0}\right| xix0 表示 x i x_{i} xi x 0 x_{0} x0 之差的绝对值,且题目中 n n n 个人的居住地点坐标均为整数。

输入格式

第一行一个正整数 T T T,表示测试数据的组数。

接下来对于每组测试数据(注意:每组测试数据有 3 3 3 行数据,以下共 3 × T 3\times T 3×T 行数据):

第一行一个正整数 n n n,表示总官员人数。

第二行共 n n n 个非负整数 x 1 , x 2 , … , x n x_{1},x_{2},\dots,x_{n} x1,x2,,xn 分别表示这 n n n 个人在数轴上的坐标。

第三行共 n n n 个非负整数 t 1 , t 2 , … , t n t_{1},t_{2},\dots,t_{n} t1,t2,,tn 分别表示这 n n n 个人出发前的打扮时间。

输出格式

共输出 T T T 行数据,对于每组测试数据,输出一行一个实数(如果是整数按整数输出,如果有小数,保留 1 1 1 位小数输出),表示使宴会开始时间最早的最优举办地点坐标 x 0 x_{0} x0。(很显然, x 0 x_{0} x0 都是唯一的)

输入输出样例 #1

输入 #1

7
1
0
3
2
3 1
0 0
2
1 4
0 0
3
1 2 3
0 0 0
3
1 2 3
4 1 2
3
3 3 3
5 3 3
6
5 4 7 2 10 4
3 2 5 1 4 6

输出 #1

0
2
2.5
2
1
3
6

说明/提示

样例说明

初始时刻为 0 0 0

对于第一组测试数据只有 1 1 1 个人,坐标为 0 0 0,打扮时间为 3 3 3,很显然 x 0 x_{0} x0 就定在坐标 0 0 0 处,使得宴会开始时间为 3 3 3 且最早。

对于第二组测试数据有 2 2 2 个人,坐标分别为 3 3 3 1 1 1,打扮时间均为 0 0 0,很显然 x 0 x_{0} x0 定在坐标 2 2 2 处,使得宴会开始时间为 1 1 1 且最早。

对于第三组测试数据有 2 2 2 个人,坐标分别为 1 1 1 4 4 4,打扮时间均为 0 0 0,很显然 x 0 x_{0} x0 定在坐标 2.5 2.5 2.5 处,使得宴会开始时间为 1.5 1.5 1.5 且最早。

数据范围

对于 30 % 30\% 30% 的数据, T = 1 , n ≤ 100 , 0 ≤ x i , t i ≤ 1000 T=1,n\le100,0\le x_{i},t_{i}\le1000 T=1,n100,0xi,ti1000

对于 60 % 60\% 60% 的数据, n ≤ 1 0 4 , 0 ≤ x i , t i ≤ 1 0 5 n\le10^{4},0\le x_{i},t_{i}\le10^{5} n104,0xi,ti105

对于 100 % 100\% 100% 的数据, 1 ≤ T ≤ 1 0 3 , n ≤ 1 0 5 , 0 ≤ x i , t i ≤ 1 0 8 1\le T\le10^{3},n\le10^{5},0\le x_{i},t_{i}\le10^{8} 1T103,n105,0xi,ti108,且保证所有测试数据的 n n n 加起来不超过 2 × 1 0 5 2\times10^{5} 2×105

解题思路

首先把所有人按照坐标从小到大的顺序排序(相当于从左到右在大街上排列),这个就不多说了。

我们要排除一种情况,就是聚会地点坐标比所有的 “坊” 都大或者都小的情况。我们用贪心的思想去看待这个问题:如果当前聚会地点坐标比所有的 “坊” 都大或者都小(我们以都大举例),然后我把这个地点移动到坐标最大的 “坊” 的位置,那么大家的移动距离都变小了,所需的时间也就都变小了,最终答案肯定也会变小。

看看答案是怎么计算的,先计算每一个人到达聚会地点的时间,我们现在假设聚会地点的坐标为 p o s pos pos i i i 是 “坊” 的编号,如果 x i ≤ p o s x_i \le pos xipos,那么这个人到达聚会地点所需要的时间就是 p o s − ( x i − t i ) pos - (x_i - t_i) pos(xiti);反之,如果 x i ≥ p o s x_i \ge pos xipos,所需的时间就是 ( x i + t i ) − p o s (x_i + t_i) - pos (xi+ti)pos

那么我们再从整体的视角上看这个问题,假设 i i i 是聚会地点左边(坐标更小)的某一个 “坊” 的坐标, j j j 是聚会地点右边(坐标更大)的某一个 “坊” 的坐标。那么左边就需要 p o s − m i n ( x i − t i ) pos - min(x_i - t_i) posmin(xiti) 的时间才能让所有人到达聚会地点;同理,右边就需要 m a x ( x j + t j ) − p o s max(x_j + t_j) - pos max(xj+tj)pos 的时间,那么所有人到达聚会地点的时间就是二者之间的最大值。

最后,怎么确定 p o s pos pos 的位置呢?先维护好两个数组 L [ i ] L[i] L[i] R [ i ] R[i] R[i] L [ i ] L[i] L[i] 表示坐标小于等于第 i i i 个 “坊” 的所有的 “坊” 的 ( x − t ) (x-t) (xt) 的最小值。 R [ i ] R[i] R[i] 表示坐标大于 i i i 个 “坊” 的所有的 “坊” 的 ( x + t ) (x+t) (x+t) 的最大值。我们预先处理出来,用 for 循环从前从后扫一遍就行。然后现在我们枚举一个正整数 k k k,表示聚会地点在第 k k k 个和第 k + 1 k + 1 k+1 个 “坊” 之间。这个位置越靠近 k k k,那么右边花费的时间就越多;相同的,越靠近 k + 1 k + 1 k+1,左边花费的时间就更多,因为我们的目标是最大值最小,所以我们的目标是尽可能实现 p o s − L [ k ] = R [ k ] − p o s pos - L[k] = R[k] - pos posL[k]=R[k]pos,这个情况下答案是最小的,否则左边花费的时间或者右边花费的时间就会变大,最终答案也会变大。那么如果上面的这个式子成立不了怎么办(求出来的 p o s pos pos 不在 k k k k + 1 k + 1 k+1 之间)。

例如求出来 p o s < x k pos < x_k pos<xk,那么说明左边花费的时间一定大于右边,最终答案是左边花费的时间,那么我们就让 p o s = x k pos = x_k pos=xk 就行了; p o s > x k pos > x_k pos>xk 同理。

时间复杂度就是排序的时间复杂度。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e5 + 5;
int n, L[maxn], R[maxn];struct Person // 用结构体方便排序
{int x, t;
} a[maxn];void solve()
{double min_t = 1000000000, ans;cin >> n;L[0] = 1000000000;R[0] = -1000000000;for (int i = 1; i <= n; i++)cin >> a[i].x;for (int i = 1; i <= n; i++)cin >> a[i].t;if (n == 1) // 只有一个人就直接在他家开聚会{cout << a[1].x << '\n';return;}sort(a + 1, a + n + 1, [](const Person &x, const Person &y){ return x.x < y.x; }); // 排序,这里我偷懒了,大家自己写一个 cmp 就行,让坐标从小到大排序for (int i = 1; i < n; i++) // 一个是左边一个是右边,所以一个从前往后,一个从后往前L[i] = min(L[i - 1], a[i].x - a[i].t);for (int i = n; i >= 2; i--)R[i - 1] = max(R[i], a[i].x + a[i].t);for (int i = 1; i < n; i++){double b = ((double)L[i] + R[i]) / 2, pos; // b 就是求出来的 posif (b < a[i].x)pos = a[i].x;if (b > a[i + 1].x)pos = a[i + 1].x;if (b >= a[i].x && b <= a[i + 1].x)pos = b;if (max(pos - L[i], R[i] - pos) < min_t) // 如果答案更优就记录一下min_t = max(pos - L[i], R[i] - pos), ans = pos;}cout << ans << '\n';
}int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int T;cin >> T;while (T--) // 多组输入solve();return 0;
}

[CSP-J2022 山东] 部署

题目背景

受疫情影响,山东省取消了 CSP-J 2022 认证活动,并于次年三月重新命题,在省内补办比赛。

题目描述

“万里羽书来未绝,五关烽火昼仍传。”

古时候没有现代信息化战争的技术,只能靠烽火传信和将军运筹帷幄的调兵遣将来取得战争的优势。

为了使消耗最低,现在 A 国已经在 n n n 个城市之间建好了道路和行军部署渠道,使得这 n n n 个城市都能互相到达且不存在环(即构成以 1 1 1 号城市为根节点的树型结构)。每个城市都驻扎了一定数量的兵力。

为了清晰的描述问题,我们给这 n n n 个城市进行 1 1 1 n n n 的编号,且 1 1 1 号城市为树的根节点(数据保证:构成以 1 1 1 号城市为根节点的一棵树)。初始时,第 i i i 座城市拥有初始兵力 a i a_{i} ai

现在为测试战争部署速度,将军进行了 m m m 次测试,每次测试可能为以下两种命令的某一种:

1 x y(三个数间均用 1 个空格分开):向 x x x 号城市以它为根的子树中的所有城市全部增兵 y y y 的数量。

2 x y(三个数间均用 1 个空格分开):向 x x x 号城市与它直接相连(含父结点和子结点)的城市全部增兵 y y y 的数量。

m m m 条命令发布出去后,将军喊来参谋,进行了 q q q 次询问,每次询问第 x x x 座城市的最终兵力情况。
该参谋就是小虾米,他又向你求助了,请你帮助他解决问题( q q q 次询问的结果)。

输入格式

第一行一个正整数 n n n 表示城市数量。

第二行一共 n n n 个正整数 a 1 , a 2 , … a n a_{1},a_{2},\dots a_{n} a1,a2,an 表示每座城市的初始兵力。

接下来 n − 1 n-1 n1 行,每行两个整数 x , y x,y x,y,表示 x x x y y y 城市之间有直接相连的道路。

接下来一行一个正整数 m m m,表示 m m m 次命令。

接下来 m m m 行,每行三个正整数 p , x , y p,x,y p,x,y 表示两种命令其中一种,其中 p = 1 p=1 p=1 时表示第一种命令, p = 2 p=2 p=2 时表示第二种命令。

接下来一行一个正整数 q q q,表示 q q q 次询问。

接下来 q q q 行,每行一个正整数 x x x ,表示询问编号为 x x x 的城市最后的兵力值。

输出格式

一共 q q q 行,每行一个正整数分别对应于每次询问的答案。

输入输出样例 #1

输入 #1

5
1 2 3 4 5
1 2
1 3
2 4
3 5
4
1 1 2
2 2 3
1 3 3
2 5 1
4
1
2
3
4

输出 #1

6
7
9
9

输入输出样例 #2

输入 #2

4
1 1 1 1
1 2
1 3
1 4
1
1 1 1
2
1
2

输出 #2

2
2

说明/提示

数据范围

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 1000 1\le n\le1000,1\le m\le1000 1n1000,1m1000

对于 60 % 60\% 60% 的数据, 1 ≤ n ≤ 1 0 5 , 1 ≤ m ≤ 1 0 5 , 1 ≤ q ≤ 1 0 5 1\le n\le10^{5},1\le m\le10^{5},1\le q\le10^{5} 1n105,1m105,1q105

其中 10 % 10\% 10% 的数据树是一条链,另外 10 % 10\% 10% 的数据只有 1 1 1 操作,另外 10 % 10\% 10% 的数据只有 2 2 2 操作。

对于 100 % 100\% 100% 的数据,数据保证给定的城市和道路能形成树,且 1 1 1 号城市为根节点。 1 ≤ n ≤ 1 0 6 , 1 ≤ m ≤ 1 0 6 , 1 ≤ q ≤ 1 0 6 , 1 ≤ a i ≤ 1 0 9 , 1 ≤ x ≤ n , 1 ≤ y ≤ 10 1\le n\le10^{6},1\le m\le10^{6},1\le q\le10^{6},1\le a_{i}\le10^{9},1\le x\le n,1\le y\le10 1n106,1m106,1q106,1ai109,1xn,1y10

解题思路

首先我们先要简化一下这 m m m 次操作,例如:1 1 3 1 1 2 直接变成 1 1 5 就可以了,1 的子树全部加 3,然后 1 的子树再加 2,不就等价于一次加 5 嘛,然后 2 操作同理。

接下来就是代码问题了,只要你会用代码实现上面两种操作就行了,至于时间复杂度是很宽容的,1 操作只需要一次 O(n) 的 d f s dfs dfs 就行了,至于 2 操作这里给大家稍微简单的证明一下。
在这里插入图片描述
对于树上这样的一条边来说,最多会存在两次操作: u u u v v v 加上一个值、 v v v u u u 加上一个值,2 操作导致一个点让一个他相邻的点的值加上一个值的操作最多出现 2 × ( n − 1 ) 2 \times (n - 1) 2×(n1) 次,也是线性的。具体细节大家看下面的代码吧。

#include <bits/stdc++.h>using namespace std;const int maxn = 1e6 + 5;
int n, m, q, tot, head[maxn], down[maxn], near[maxn], num[maxn];
// down[x] 表示 x 和他的子树都要加上 down[x]
// near[x] 表示 x 和他周围的点要加上的数
// num[x] 表示这个点上有多少士兵struct edge // 这里建图用的是链式前向星,不会的自行学习
{int nxt, to;
} e[maxn << 1];void addedge(int u, int v)
{e[++tot].nxt = head[u];e[tot].to = v;head[u] = tot;
}void solve1(int x, int fa, int k) // 1 操作
{k += down[x]; // 因为是 dfs,所以下面遍历的都是子树,全部需要加 knum[x] += k; for (int i = head[x]; i; i = e[i].nxt) // 向下遍历{int v = e[i].to;if (v == fa) // 不能往上走continue;solve1(v, x, k);}return;
}int main()
{cin >> n;for (int i = 1; i <= n; i++)cin >> num[i];for (int i = 1; i < n; i++){int u, v;cin >> u >> v;addedge(u, v);addedge(v, u);}cin >> m;for (int i = 1; i <= m; i++) // 先预处理一下,我们上面说的第一步{int op, x, y;cin >> op >> x >> y;if (op == 1)down[x] += y;if (op == 2)near[x] += y;}solve1(1, 0, 0); // 1 操作for (int i = 1; i <= n; i++) // 2 操作{num[i] += near[i];for (int j = head[i]; j; j = e[j].nxt) // 枚举与 i 相邻的点{int v = e[j].to;num[v] += near[i];}}cin >> q;for (int i = 1; i <= q; i++) // 问哪个输出哪个{int id;cin >> id;cout << num[id] << '\n';}return 0;
}

[CSP-J2022 山东] 吟诗

题目背景

受疫情影响,山东省取消了 CSP-J 2022 认证活动,并于次年三月重新命题,在省内补办比赛。

题目描述

“文章本天成,妙手偶得之。”

吟诗是表达情怀的常用手段,战争落下了帷幕,常年的军旅生活使得小虾米喜欢上了豪放派的诗歌。

这一天,小虾米突然想吟诗了。著名的豪放派诗人苏轼有“老夫聊发少年狂,左牵黄,右擎苍。”的豪放,又有“十年生死两茫茫,不思量,自难忘。”的悲怆。小虾米心向往之,于是也想用《江城子》词牌名作诗。

小虾米想作出能流传千古的诗,根据经验,如果一首诗存在妙手就能流传千古。

具体来说,一首 N 个字的诗,每个字可以用 1 1 1 10 10 10 之间的某个正整数来表示。同时存在三个正整数 X , Y , Z ( 1 ≤ X ≤ 7 , 1 ≤ Y ≤ 5 , 1 ≤ Z ≤ 5 ) X,Y,Z\left(1\le X\le7,1\le Y\le5,1\le Z\le5\right) X,Y,Z(1X7,1Y5,1Z5),如果诗中出现了三个连续的片段使得第一个片段之和为 X X X,第二个片段之和为 Y Y Y,第三个片段之和为 Z Z Z,则小虾米认为这首诗出现了妙手

即长度为 n n n 的序列 a 1 , a 2 , … a n ( 1 ≤ a i ≤ 10 ) a_{1},a_{2},\dots a_{n} \left(1\le a_{i}\le10\right) a1,a2,an(1ai10),如果存在 i , j , k , l ( 1 ≤ i < j < k < l ≤ n ) i,j,k,l\left(1\le i<j<k<l\le n\right) i,j,k,l(1i<j<k<ln) 使得 a i + a i + 1 + … a j − 1 = X a_{i}+a_{i+1}+\dots a_{j-1}=X ai+ai+1+aj1=X a j + a j + 1 + … a k − 1 = Y a_{j}+a_{j+1}+\dots a_{k-1}=Y aj+aj+1+ak1=Y a k + a k + 1 + … a l − 1 = Z a_{k}+a_{k+1}+\dots a_{l-1}=Z ak+ak+1+al1=Z 同时成立,则认为序列出现了妙手(注:第二个片段紧接第一个片段,第三个片段紧接第二个片段)。

举例来说,如果 N = 7 N=7 N=7 X = 7 X=7 X=7 Y = 3 Y=3 Y=3 Z = 3 Z=3 Z=3,则所有长度为 7 7 7 的序列中,很显然共有 1 0 7 10^{7} 107 种序列,其中一种序列 [ 1 , 5 , 2 , 2 , 1 , 3 , 4 ] \left[1,5,2,2,1,3,4\right] [1,5,2,2,1,3,4] 出现了妙手,因为存在三个连续的区间 [ 2 , 3 ] \left[2,3\right] [2,3] [ 4 , 5 ] \left[4,5\right] [4,5] [ 6 , 6 ] \left[6,6\right] [6,6] 满足它们的和分别为 X = 7 X=7 X=7 Y = 3 Y=3 Y=3 Z = 3 Z=3 Z=3

小虾米想知道在给定 N , X , Y , Z N,X,Y,Z N,X,Y,Z 的前提下(共计 1 0 n 10^{n} 10n 种序列,即共 1 0 n 10^{n} 10n 种诗),计算有多少种存在妙手的诗,请你帮他计算出答案。

由于答案可能很大,请你将结果对 998244353 998244353 998244353 取模。

输入格式

一行,以空格隔开的 4 个正整数 N , X , Y , Z N,X,Y,Z N,X,Y,Z,分别表示序列长度和题目中 X , Y , Z X,Y,Z X,Y,Z 的值。

输出格式

一行,一个整数,表示答案对 998244353 998244353 998244353 取模的结果。

输入输出样例 #1

输入 #1

3 2 3 3

输出 #1

1

输入输出样例 #2

输入 #2

4 7 5 5

输出 #2

34

输入输出样例 #3

输入 #3

23 7 3 5

输出 #3

824896638

说明/提示

样例一说明

在所有可能的序列中,只能构造出一种序列 [ 2 , 3 , 3 ] \left[2,3,3\right] [2,3,3] 满足题意,因此答案为 1 1 1

数据范围

对于 30 % 30\% 30% 的数据, 3 ≤ N ≤ 5 3\le N\le5 3N5

对于 60 % 60\% 60% 的数据, 3 ≤ N ≤ 20 3\le N\le20 3N20

对于 100 % 100\% 100% 的数据, 3 ≤ N ≤ 40 , 1 ≤ X ≤ 7 , 1 ≤ Y ≤ 5 , 1 ≤ Z ≤ 5 3\le N\le40,1\le X\le7,1\le Y\le5,1\le Z\le5 3N40,1X7,1Y5,1Z5

解题思路

这一把还没考 d p dp dp 呢,所以肯定是 d p dp dp(狗头)。

这一题比较有防 AK 的意思哈,观察数据范围,有可能是一个状态压缩 d p dp dp(实际上就是)。首先我们需要明确一点,计算满足条件的序列数量其实是不太好整的,因为一个序列里面可能出现好几个 “妙手”,那咋办呢?你计算不满足条件的序列数量不就行了嘛,然后拿全部的数量减一下就是答案。

然后我们设计状态 d p [ i ] [ j ] dp[i][j] dp[i][j],其中 i i i 表示前 i i i 个数字, j j j 表示一个压缩的状态。我们来详细说一下 j j j j j j 是一个二进制数,表示的是前 i i i 个数有哪些后缀和。例如:序列 [ 1 , 2 , 3 , 4 ] [1, 2, 3, 4] [1,2,3,4],有 4 、 7 、 9 、 10 4、7、9、10 47910(4,4 + 3,4 + 3 + 2,4 + 3 + 2 + 1) 一共 4 种后缀和,那么就让 j = 1101001000 j = 1101001000 j=1101001000(二进制,最右边一位表示后缀和 1 在不在)。那么 d p [ i ] [ j ] dp[i][j] dp[i][j] 就是序列的前 i i i 个数,后缀和满足 j j j 有多少种可能(只是前 i i i 个数字)。

那么怎么判断是不是我们想要的序列呢?假设 X = 3 , Y = 2 , Z = 1 X = 3,Y = 2,Z = 1 X=3Y=2Z=1,如果在 d p dp dp 的时候,发现同时存在后缀和 1 、 3 、 6 1、3、6 136 的话,是不是就能说明存在一个 “妙手” 呢?如果遇到这种情况我们不统计就可以了。

最后我们讨论一下如何进行状态转移,显然我们应该按照 i i i 从小到大的顺序进行转移,那么假设当前我们枚举到了序列的前 i i i 个,再枚举这一位是几,假设是 k k k,然后第三个数,我们枚举前 i − 1 i - 1 i1 个数后缀和的情况,令其为 j j j。因为我们加上了一个 k k k,所以之前的后缀和都应该加上 k k k,等价于 j j j 左移 k k k 位,又因为 k k k 本身也算一个后缀和,所以再让 j j j 或上一个 ( 1 < < ( k − 1 ) ) (1 << (k - 1)) (1<<(k1)) 表示 k k k 这个后缀和也存在,得到的值假设为 s s s,那么就有了下面的转移方程。
s = ( ( j < < k ) ∣ ( 1 < < ( k − 1 ) ) ) s = ((j << k) | (1 << (k - 1))) s=((j<<k)(1<<(k1)))
d p [ i ] [ s ] = d p [ i ] [ s ] + d p [ i − 1 ] [ j ] dp[i][s] = dp[i][s] + dp[i - 1][j] dp[i][s]=dp[i][s]+dp[i1][j]

因为 j j j 不需要储存大于 ( X + Y + Z ) (X+Y+Z) (X+Y+Z) 的后缀和(没意义,不能帮助我们确定是否存在 “妙手”),所以我们要保证 j ≤ ( 1 < < ( x + y + z ) ) − 1 j \le (1 << (x + y + z)) - 1 j(1<<(x+y+z))1

#include <bits/stdc++.h>using namespace std;const int maxn = 45, MOD = 998244353;
int n, x, y, z, dp[maxn][1 << 18];int main()
{long long ans = 1;cin >> n >> x >> y >> z;int all = (1 << (x + y + z)) - 1, a = ((1 << (z - 1)) | (1 << (y + z - 1)) | (1 << (x + y + z - 1))); // 要保证压缩的状态小于等于 all,大家注意 a 的含义,如果一个状态包含 a,说明存在妙手dp[0][0] = 1; // 初始状态for (int i = 1; i <= n; i++) // 前 i 个数{ans = ans * 10 % MOD; // 所有的状态数,顺道求了for (int j = 0; j <= all; j++) // 前 i - 1 个数的后缀和状态for (int k = 1; k <= 10; k++) // 当前这个数是多少{int s = ((j << k) | (1 << (k - 1))) & all; // &all 是为了保证去掉溢出的内容,让状态值小于 allif ((s | a) != s) // 说明 s 是一个不存在妙手的状态dp[i][s] = (dp[i][s] + dp[i - 1][j]) % MOD; // 记得取模}}for (int i = 0; i <= all; i++) // 去掉所有不合法的状态ans = (ans + MOD - dp[n][i]) % MOD; // +MOD 防止 ans 被减成负数cout << ans;return 0;
}










本人能力有限,如有不当之处敬请指教!

关键字:河北招投标信息网官网_武汉建设学校_百度推广客服工作怎么样_常州百度seo排名

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: