Codeforces Round 924 E. Modular Sequence

2024-02-13 08:20

本文主要是介绍Codeforces Round 924 E. Modular Sequence,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

E. Modular Sequence

E

题意

对于一个长度为 n n n 的数组 a a a,定义它是 g o o d good good 的当且仅当:

  • a 1 = x a_1 = x a1=x
  • a i = a i − 1 + y a_{i} = a_{i - 1} + y ai=ai1+y a i = a i − 1 m o d y i ≥ 2 a_{i} = a_{i - 1} mod \hspace{3pt} y \quad i \geq 2 ai=ai1modyi2

给定 n 、 x 、 y 、 S n、x、y、S nxyS,问能否构造出一个 S S S 的长度为 n n n g o o d good good 数组

思路

注意到后面每个放置的 a i a_i ai,它们模除 y y y 的值都是一样的,因为自从 a 1 = x a_1 = x a1=x 后,后面每次变化都是同余 y y y 的。

定义 r = x m o d y r = x \hspace{3pt} mod \hspace{3pt} y r=xmody,那么我们先将数组的 n n n 个位置填成: a i = r a_i = r ai=r a 1 = x a_1 = x a1=x,先计算一下这样子最小的数组和是否超过 S S S,超过的话显然没有答案。对于没有超过 S S S 的时候,我们后面肯定是要在 r r r 的基础上放置若干个 y y y,那么剩余要放置的和应该能被 y y y 整除 才行。

假设要放置 l e f t S u m y leftSum \over y yleftSum y y y,我们就一定要尽可能用最短的长度构造出来这么多个 y y y 的放置方式,考虑 D P DP DP,用 d p k dp_k dpk 表示要从空序列开始放置 k k k 个物品,最短需要的长度,那么转移我们可以枚举最后使用的完整上升长度 l e n len len,这个上升长度必须从 0 0 0 开始,长度为 l e n len len,例如: . . . . , 0 , 1 , 2 , . . . , l e n − 1 ...., 0, 1, 2, ..., len - 1 ....,0,1,2,...,len1 这样一个结尾方式,那么它的子状态就是: d p [ k − l e n ⋅ ( l e n − 1 ) 2 ] dp[k - \frac{len \cdot (len - 1)}{2}] dp[k2len(len1)],因为最后这一段上升序列的贡献是 l e n ⋅ ( l e n − 1 ) 2 \frac{len \cdot (len - 1)}{2} 2len(len1),长度为 l e n len len,我们取 m i n min min 值就可以

对于多组样例,我们只需要关心最后要放置多少个 y y y,所以我们可以预计算 d p dp dp 数组

我们先枚举一开始是否需要先跟着 x x x 递增一段长度,然后再从 0 0 0 开始构造答案,因为我们的 d p dp dp 数组是从 0 0 0 开始构造答案的最短长度,假设一开始从 x x x 开始递增了 k k k 次(不包含 a 1 a_1 a1 ),也就是数组开头为: x , x + y , x + 2 y , . . . , x + k y x, x + y, x + 2y, ..., x + ky x,x+y,x+2y,...,x+ky,这样子的形式,我们得先确保这个 p r e S u m preSum preSum 不会超过 S S S,然后在此基础上计算后面还需要放置的和的余量 l e f t S u m = S − p r e S u m leftSum = S - preSum leftSum=SpreSum,当然这个余量要先预算一下后面每个位置放一个 r r r 的花销, l e f t S u m − = r × ( n − 1 − k ) leftSum -= r \times (n - 1 - k) leftSum=r×(n1k),最后的这个 l e f t S u m leftSum leftSum 不能为负数,并且要能被 y y y 整除,还要要求 d p [ l e f t S u m y ] dp[{leftSum \over y}] dp[yleftSum] 的值小于等于 n − 1 − k n - 1 - k n1k,意思是我们剩余的位置必须要足够放置这么多个 y y y,我们才能在这个基础上构造答案

如果上述条件都符合,我们现在中间多余的用不上的位置 [ k + 1 , n − d p [ l e f t S u m y ] ] [k + 1, n - dp[{leftSum \over y}]] [k+1,ndp[yleftSum]] 先填补上 r r r,然后从 n − d p [ l e f t S u m y ] + 1 n - dp[{leftSum \over y}] + 1 ndp[yleftSum]+1 这个位置开始构造答案,我们用一个 l e n len len 数组来暂存一下我们的答案构造的上升长度序列,枚举转移的路径即可

时间复杂度: O ( S S ) O(S \sqrt{S}) O(SS )

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;const int INF=0x3f3f3f3f;
const long long INFLL=1e18;typedef long long ll;const int N = 200050;int dp[N];void solve(){int n;ll x, y, S;std::cin >> n >> x >> y >> S;fore(k, 0, n){ //除了首项x外,后面还跟着递增了k次ll preSum = (k + 1) * x + k * (k + 1) / 2 * y; //前缀和if(preSum > S) continue;ll leftSum = S - preSum; //后面需要构造的和leftSum -= (n - 1 - k) * (x % y); //先减去模数 x % yif(leftSum < 0 || leftSum % y) continue;int needSum = leftSum / y; //后面需要多少个yif(dp[needSum] > n - k - 1) continue; //长度不够构造std::vector<int> a(n);a[0] = x; //首项fore(i, 1, k + 1) a[i] = a[i - 1] + y; //起初跟着长度为k的一个递增fore(i, k + 1, n - dp[needSum]) a[i] = x % y; //中间这里用不上,我们在 n - dp[needSum] -> n - 1构造int idx = n - dp[needSum];std::vector<int> len;while(needSum){for(int l = 2; l * (l - 1) / 2 <= needSum; ++l)if(dp[needSum] == dp[needSum - l * (l - 1) / 2] + l){ //这是一个最短长度的转移len.push_back(l); //记录构成最短长度的转移needSum -= l * (l - 1) / 2; //剩余需要放置的y的数量break;}}for(auto l : len) //按照答案转移放置yfore(j, 0, l) //从0开始放置,放 l 次a[idx++] = x % y + j * y; //记住底是 x % ystd::cout << "YES\n";for(auto el : a) std::cout << el << ' ';std::cout << endl;return;}std::cout << "NO\n";
}int main(){std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout.tie(nullptr);fore(i, 1, N) dp[i] = INF;fore(k, 1, N)   //dp[i] 表示要放 i 个 y 需要的最短长度是多长for(int len = 2; len * (len - 1) / 2 <= k; ++len) //这里len是从0开始放的,所以最后一位是len - 1,长度为lendp[k] = std::min(dp[k], dp[k - len * (len - 1) / 2] + len);int t;std::cin >> t;while(t--){solve();}return 0;
}

这篇关于Codeforces Round 924 E. Modular Sequence的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/704998

相关文章

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

Codeforces 482B 线段树

求是否存在这样的n个数; m次操作,每次操作就是三个数 l ,r,val          a[l] & a[l+1] &......&a[r] = val 就是区间l---r上的与的值为val 。 也就是意味着区间[L , R] 每个数要执行 | val 操作  最后判断  a[l] & a[l+1] &......&a[r] 是否= val import ja

Codeforces Round 971 (Div. 4) (A~G1)

A、B题太简单,不做解释 C 对于 x y 两个方向,每一个方向至少需要 x / k 向上取整的步数,取最大值。 由于 x 方向先移动,假如 x 方向需要的步数多于 y 方向的步数,那么最后 y 方向的那一步就不需要了,答案减 1 代码 #include <iostream>#include <algorithm>#include <vector>#include <string>

浙大数据结构:02-线性结构4 Pop Sequence

这道题我们采用数组来模拟堆栈和队列。 简单说一下大致思路,我们用栈来存1234.....,队列来存输入的一组数据,栈与队列进行匹配,相同就pop 机翻 1、条件准备 stk是栈,que是队列。 tt指向的是栈中下标,front指向队头,rear指向队尾。 初始化栈顶为0,队头为0,队尾为-1 #include<iostream>using namespace std;#defi

【UVA】1626-Brackets sequence(动态规划)

一道算是比较难理解的动规。 状态转移分2个: (用d[i][j]表示在i~j内最少需要添加几个括号,保持平衡) 1.如果s[i]和s[j]是一对括号,那么d[i][j] = d[i + 1][j - 1] 2.否则的话 d[i][j] = min(d[i][k],[k + 1][j]); 边界是d[i + 1][i] = 0; d[i][i] = 1; 13993644 162

【UVA】10534 - Wavio Sequence(LIS最长上升子序列)

这题一看10000的数据量就知道必须用nlog(n)的时间复杂度。 所以特意去看了最长上升子序列的nlog(n)的算法。 如果有2个位置,该位置上的元素为A[i]和A[j],并且他们满足以下条件: 1.dp[i] = dp[j]    (dp[x]代表以x结尾的最长上升子序列长度) 2.A[i] < A[j] 3.i < j 那么毫无疑问,选择dp[i] 一定优于选择dp[j] 那么

Codeforces#295(Div.2)A、B(模拟+BFS)

解题报告链接:点击打开链接 C. 题目链接:点击打开链接 解题思路: 对于给定的字符串,取出现次数最多的字母(可以同时有多个)。由这些字母组成长度为n的字符串,求有多少种组合。最后用数学知识即可。 完整代码: #include <algorithm>#include <iostream>#include <cstring>#include <climits>