这道题是 gpt 出的,但是我做不出来,是时候被替代了。
把每个数不断除以二直到得到最小的奇数,记录一下我们能在这个数上提出来的 的个数。
首先想到的是贪心地把 全部分配给最大的,但是贪心甚至过不了样例。
但是想一想如果 的限制不存在,我们直接把所有的 都分配给最大的处理过后的数 ,那肯定是正确的。
能不能推广到这道题上面来?
当然可以,我们首先还是预处理把所有的数里的 都给提出来。
假设有一个子序列,最后一个数就是这个序列里面的最大值,那么是不是就变成了我们提到的 " 的限制不存在"的情况了?
因为显然我们会把所有的 都给最后一个数,使得答案最大。
仔细想想,这其实是一个单调栈的过程,栈里装的是我们处理过后的数字且递减,对于其中的任意一个元素 ,假设他的上一个元素师 ,那么我们一定会把 之间的所有 都乘给 ,因为显然这是这一部分 的最好分配方式了。
你会发现这样应该通过不了样例。
如果说进行完上述操作之后得到的 就大于 了呢?那么我们显然是会把 之前的所有 也都乘给 的呀!
实际上这是一个把问题不断划分成子问题的过程。
由于有 这个限制,那么我们发现,如果正序枚举,当前区间的分配方式对于全局来说,不一定就是最优的,如果后面出现了更大的数,那么当前区间的 可以转而分配给后面的区间。
如果我们倒叙枚举呢?
这时候就可以十分顺理成章地更新出最优答案,而且我们已经算出的答案是不会受前面的影响的。
可是我们显然是不能够对于每一个前缀都去倒叙枚举,这样复杂度就变成 了。
这时候就应该考虑通过继承的方式来快速计算每一个答案,当前加入一个数,我们不断地通过单调栈的模拟把他和前面的合并,直到满足 结束。
| #include<bits/stdc++.h> |
| #define int long long |
| using namespace std; |
| const int N=2e5+10,MOD=1e9+7; |
| template<typename T>inline void re(T &x) |
| { |
| x=0;int f=1;char c=getchar(); |
| while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} |
| while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} |
| x*=f; |
| } |
| template<typename T>inline void wr(T x) |
| { |
| if(x<0)putchar('-'),x=-x; |
| if(x>9)wr(x/10); |
| putchar(x%10^48); |
| } |
| inline void debug(int x){wr(x),putchar('\n');} |
| int n,T; |
| int a[N]; |
| int s[N],sc[N]; |
| int calc(int &x) |
| { |
| int ans=0; |
| while(x%2==0&&x!=0) |
| { |
| ans++; |
| x>>=1; |
| } |
| return ans; |
| } |
| inline int power(int a,int b) |
| { |
| int ans=1; |
| while(b) |
| { |
| if(b&1)ans=ans*a%MOD; |
| a=a*a%MOD; |
| b>>=1; |
| } |
| return ans%MOD; |
| } |
| int p(int a,int b) |
| { |
| int ans=1; |
| while(b) |
| { |
| if(b&1)ans=ans*a; |
| a=a*a; |
| b>>=1; |
| } |
| return ans; |
| } |
| int ans[N]; |
| int stk[N]; |
| inline void solve() |
| { |
| re(T); |
| while(T--) |
| { |
| re(n); |
| int top=0; |
| for(register int i=1;i<=n;++i) |
| { |
| re(a[i]); |
| sc[i]=calc(a[i])+sc[i-1]; |
| s[i]=(s[i-1]+a[i])%MOD; |
| while(top&&a[i]*p(2,sc[i]-sc[stk[top]])>=a[stk[top]]) |
| top--; |
| ans[i]=((ans[stk[top]]+a[i]*power(2,sc[i]-sc[stk[top]])%MOD)%MOD+(s[i-1]-s[stk[top]])+MOD)%MOD; |
| stk[++top]=i; |
| wr(ans[i]),putchar(' '); |
| } |
| putchar('\n'); |
| } |
| } |
| signed main() |
| { |
| solve(); |
| return 0; |
| } |
注意在比较 和 的时候不能取模,不然有失正确性,但是由于数据不太强,仍然可以通过前 个点。
这个点是一个非常好的hack,如下:
| Input: |
| 1 |
| 3 |
| 999999998 2097152 1048576 |
| Expected output: |
| 999999998 2097143 546480318 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!