Codeforces Round 924 E. Modular Sequence

文章讲述了如何构造一个长度为n的数组,满足特定的递推规则,即数组元素要么等于前一项加y,要么等于前一项除以y的余数。通过定义变量和动态规划策略,解决给定n、x、y和S的情况下,是否存在这样的好数组的问题。
摘要由CSDN通过智能技术生成

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 % y
        if(leftSum < 0 || leftSum % y) continue;
        
        int needSum = leftSum / y; //后面需要多少个y
        if(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) //按照答案转移放置y
            fore(j, 0, l) //从0开始放置,放 l 次
                a[idx++] = x % y + j * y; //记住底是 x % y
        
        std::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,长度为len
            dp[k] = std::min(dp[k], dp[k - len * (len - 1) / 2] + len);

    int t;
    std::cin >> t;
    while(t--){
        solve();
    }
    return 0;
}
根据提供的引用内容,Codeforces Round 511 (Div. 1)是一个比赛的名称。然而,引用内容中没有提供与这个比赛相关的具体信息或问题。因此,我无法回答关于Codeforces Round 511 (Div. 1)的问题。如果您有关于这个比赛的具体问题,请提供更多的信息,我将尽力回答。 #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces Round 867 (Div. 3)(A题到E题)](https://blog.csdn.net/wdgkd/article/details/130370975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值