【题解】[CF1753E] N Machines

· · 题解

场上口胡了,但是只剩 20 分钟了,最后没写完……

看一下题目中初始结果不超过 $2\times 10^9$ 的限制,很明显乘法操作就很少了。考虑对于操作序列,将乘法放在前面,加法放在后面所得结果最小,但是最后结果也肯定不小于乘法操作的 $a_i$ 之积。所以 $a_i>1$ 的乘法操作最多也就 $\log_2 2\times 10^9 = 31$ 次。 考虑修改的策略,肯定是将一些乘法操作移到末尾,一些加法操作移到开头。 于是可以写出暴力:枚举将哪些乘法操作移到末尾,然后对于所有加法操作,算出将其移到开头的价值,选出最大的若干个。考虑到乘法操作有 $O(\log A)$ 个,加法操作有 $O(n)$ 个,最后复杂度就为 $O(An)$。显然跑不过…… 于是发掘性质: 1. 对于 $a_i$ 相同的乘法操作,肯定选前面的进行操作。证明:对于 $a_i$ 相同的操作,选前面的能够影响到更多的加法操作,一定不劣。 2. 对于所有加法操作,按其在第几个乘法之后分段,每一段肯定会选 $a_i$ 大的。证明:对于加法操作将其放在开头之后带来的贡献均可以表示为:$X\times a_i$,其中 $X$ 之与当前操作在第几段有关,所以对于每一段,肯定要选 $a_i$ 大的。 性质 1 直接让爆搜的状态数变得很少,貌似不会超过 $2^{11}$。 爆搜出哪些乘法被放在末尾以后,再根据性质 2 来快速求出加法操作的贡献:将每一段的 $a_i$ 排序,假设当前还可以移动 $k$ 个加法操作,二分这 $k$ 次操作做出的最小贡献 $mid$,然后对于每一段,二分出贡献不小于 $mid$ 的有多少个 $a_i$,然后看可选的位置数量 $cnt$ 是否不小于 $mid$ 来完成二分。 然后需要维护每一段排序后 $a_i$ 的前缀和来统计答案。 总时间复杂度 $O(2^{11} \times \log_n^2)$。 ```c++ #include<bits/stdc++.h> using namespace std; typedef long long ll; inline int in(){ int x; scanf("%d",&x); return x; } const int N=1e6+5; int n,m,o,A,B,C,all; int a[N],b[N]; map<int,int> id; vector<int> v1[100],v2[100]; vector<ll> s[100]; int mark[N]; int cnt;ll sum,ans; void check(ll X){ ll now=all; cnt=sum=0; for(int i=0;i<=m;i++){ if(i&&!mark[i])now/=b[i]; if(all==now)continue; int l=0,r=v2[i].size()-1,mid; while(l<=r){ mid=l+r>>1; if(1ll*v2[i][mid]*(all-now)>=X)l=mid+1; else r=mid-1; } cnt+=l;if(l>0)sum+=s[i][l-1]*(all-now); } } void calc(int D){ ll l=0,r=1e18,mid,res; D/=A; int now=all;res=all; for(int i=0;i<=m;i++){ if(i&&!mark[i])now/=b[i]; if(s[i].size()){ res+=s[i].back()*now; } } while(l<=r){ mid=l+r>>1; check(mid); if(cnt<=D)r=mid-1; else l=mid+1; } check(r); res+=sum-max(0,cnt-D)*r; ans=max(ans,res); } void dfs(int x,int y){ if(1ll*y*B>C)return; if(x==o+1){ calc(C-y*B); return; } dfs(x+1,y); for(int p:v1[x]){ y++; mark[p]=1; dfs(x+1,y); } for(int p:v1[x])mark[p]=0; } int main(){ n=in(),C=in(),A=in(),B=in(); ans=1,all=1; for(int i=1;i<=n;i++){ char op=getchar(); while(op!='*'&&op!='+')op=getchar(); scanf("%d",&a[i]); if(op=='*'){ if(a[i]>1){ ans*=a[i]; if(!id[a[i]])id[a[i]]=++o; m++;b[m]=a[i];all*=b[m]; v1[id[a[i]]].push_back(m); } }else ans+=a[i],v2[m].push_back(a[i]); } for(int i=0;i<=m;i++){ sort(v2[i].begin(),v2[i].end(),greater<int>()); s[i]=vector<ll>(v2[i].size(),0); for(int j=0;j<s[i].size();j++){ s[i][j]=v2[i][j]; if(j)s[i][j]+=s[i][j-1]; } } dfs(1,0); cout<<ans<<endl; return 0; } ```