CF2035D
CF2035D
这道题是 gpt 出的,但是我做不出来,是时候被替代了。
分析
把每个数不断除以二直到得到最小的奇数,记录一下我们能在这个数上提出来的 \(2\) 的个数。
首先想到的是贪心地把 \(2\) 全部分配给最大的,但是贪心甚至过不了样例。
但是想一想如果 \(i<j\) 的限制不存在,我们直接把所有的 \(2\) 都分配给最大的处理过后的数 ,那肯定是正确的。
能不能推广到这道题上面来?
当然可以,我们首先还是预处理把所有的数里的 \(2\) 都给提出来。
特殊情况
假设有一个子序列,最后一个数就是这个序列里面的最大值,那么是不是就变成了我们提到的 "\(i<j\) 的限制不存在"的情况了?
因为显然我们会把所有的 \(2\) 都给最后一个数,使得答案最大。
初步思考
仔细想想,这其实是一个单调栈的过程,栈里装的是我们处理过后的数字且递减,对于其中的任意一个元素 \(stk_x\) ,假设他的上一个元素师 \(stk_{x-1}\) ,那么我们一定会把 \((stk_{x-1},stk_x]\) 之间的所有 \(2\) 都乘给 \(stk_x\) ,因为显然这是这一部分 \(2\) 的最好分配方式了。
进一步思考
你会发现这样应该通过不了样例。
如果说进行完上述操作之后得到的 \(stk_x'\) 就大于 \(stk_{x-1}\) 了呢?那么我们显然是会把 \(stk_{x-1}\) 之前的所有 \(2\) 也都乘给 \(stk_x\) 的呀!
实际上这是一个把问题不断划分成子问题的过程。
正序枚举
由于有 \(i<j\) 这个限制,那么我们发现,如果正序枚举,当前区间的分配方式对于全局来说,不一定就是最优的,如果后面出现了更大的数,那么当前区间的 \(2\) 可以转而分配给后面的区间。
倒序枚举
如果我们倒叙枚举呢?
这时候就可以十分顺理成章地更新出最优答案,而且我们已经算出的答案是不会受前面的影响的。
写法
可是我们显然是不能够对于每一个前缀都去倒叙枚举,这样复杂度就变成 \(O(n^2)\) 了。
这时候就应该考虑通过继承的方式来快速计算每一个答案,当前加入一个数,我们不断地通过单调栈的模拟把他和前面的合并,直到满足 \(stk_x'<stk_{x-1}\) 结束。
Code
#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;
}
小细节
注意在比较 \(stk_x'\) 和 \(stk_{x-1}\) 的时候不能取模,不然有失正确性,但是由于数据不太强,仍然可以通过前 \(24\) 个点。
\(25\) 这个点是一个非常好的hack,如下:
Input:
1
3
999999998 2097152 1048576
Expected output:
999999998 2097143 546480318
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18511552