CF1928E: Modular Sequence 题解

E:

题意:你需要构造长度为 n 的数列 a ,满足和为sum,且 a1=xai=ai1+yai1mody 。(n,sum,x,y<=2e5)


先说下我曲折的做题经历。不想看可以直接跳到solution。

题目很容易让人想到复杂度和根号有关。为了简化描述,先把题意转化为标准形式,即:已知 a1 ,每次可以加一或者归零,构造和为 sum 的数列。

我寻思 sum2log 应该可过?于是想了个bitset背包的做法,由于要涉及到当前值,我设 f[i][j] 表示最后一个数字是 i ,能否使和为 j。枚举第一次归零到最后的长度,用 sum 减去 a1 为首的等差数列就是 j 。然后却卡在了方案输出上,我一开始以为,由于靠前位置的 f[i][j] 是当前位置 f[i][j] 的子集,所以假如 f[i1][ji] 为1,我就可以让最后一个数字是 i 。后来发现这个贪心是错的,因为你无法确定 f[i][j] 这个状态具体有多长,你以为它在通过 f[i1][ji] 不断缩短,但是可能这个状态是要到更长的长度才更新的到。(没看懂很正常,因为这个贪心是我瞎想的)甚至发现不仅正确性没有,复杂度其实是 sum2sumlog 。然后看题解去了。

看到题解说可以根号分治,我发现好有道理:对任意的 sum,一定可以在根号级别就能把 a 构造出来,所以我们只需要考虑 n 小于根号的情况。然后枚举每一位数填什么也改为了枚举下一个三角形的边长。f[i][j] 表示长度为 i 的数列能不能表示出 j 来。由于 i 最多枚举到根号,因此复杂度为 sumsum·sumlog ,即,枚举位置,枚举下一个三角的边长,对 j 进行背包运算。一交啪叽TLE on test 78。这个复杂度是真不行。于是寻找更靠谱的题解。


Solution:

这是一个比较有启发性的做法。

在上面的做法中一直都在关心某个长度能不能组成 j ,这是由于我们对于输入的 sum,要根据 sum 来算出需要的 j 。但是我们发现拼三角形来达到 j 的这个过程,好像与输入的数据无关,那我们换个思路,预处理出 f[j] 表示:拼出总和 j 所需的最小长度。放弃使用bitset。

发现问题简化了不少, f[j] 可以在 sumsum 时间预处理。判断有无解时,我们可以枚举 a1 开头的等差数列长度,sum 减去开头长度的得到 j ,比较 f[j] 与剩余部分的长度。

输出方案则可以枚举最后一个三角形边长 i ,若 f[j(i+1)i2]f[j]i 说明减掉边长为 i 的三角形后依然有解。这样可以构造出整个序列 a 。

#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define FOR() ll le=e[u].size();for(ll i=0;i<le;i++) #define QWQ cout<<"QwQ\n"; #define ll long long #include <vector> #include <queue> #include <bitset> #define ls now<<1 #define rs now<<1|1 using namespace std; const ll N=201010; const ll qwq=303030; const ll inf=0x3f3f3f3f; inline ll read() { ll sum = 0, ff = 1; char c = getchar(); while(c<'0' || c>'9') { if(c=='-') ff = -1; c = getchar(); } while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); } return sum * ff; } ll T; ll n,S,p,q,base; ll da=N-10; ll f[N]; ll ans[N]; inline ll jia(ll shou,ll shu) { return (shou+shou+shu-1)*shu/2; } void qiu() { memset(f,0x3f,sizeof(f)); f[0] = 0; for(ll i=0;i<=da;i++) { for(ll j=2;;j++) { ll wo = i+jia(0,j); if(wo>da) break; f[wo] = min(f[wo],f[i]+j); } } } void calc(ll len) { for(ll i=1;i<=n-len;i++) ans[i] = q+i-1; S -= jia(q,n-len); ll now = n; while(S) { for(ll i=2;i<=len;i++) { if(S-jia(0,i)>=0 && f[S-jia(0,i)] <= f[S]-i) { S -= jia(0,i); for(ll j=i-1;j>=0;j--) ans[now--] = j; break; } } } } int main() { qiu(); T = read(); while(T--) { n = read(); q = read(); p = read(); S = read(); for(ll i=1;i<=n;i++) ans[i] = 0; if(n==1 && S==q) { cout<<"YES\n"<<q<<endl; continue; } if(n==1 || S<q) { cout<<"NO\n"; continue; } base = q%p; S -= n*base; if(S<0 || S%p) { cout<<"NO\n"; continue; } S /= p; q = q/p; bool flag = 0; for(ll i=1;i<=n;i++) { ll wo = S-jia(q,i); if(wo>=0 && f[wo]<=n-i) { calc(n-i); flag = 1; break; } } if(!flag) cout<<"NO\n"; else outing(); } return 0; }

__EOF__

本文作者枫叶晴
本文链接https://www.cnblogs.com/maple276/p/18037844.html
关于博主:菜菜菜
版权声明:呃呃呃
声援博主:呐呐呐
posted @   maple276  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示