Function(三分+预处理)
题目描述:
如果不知道三分的可以看看这个传送门
令$g(x)$为$x$中各位数和,例如$g(123456)=1+2+3+4+5+6$,然后有T次询问,每次询问给你一个$a$,$b$,$c$,$d$,$n$
让你求$f(x)=Ax^{2}g(x)+Bx^{2}+Cxg^{2}(x)+Dxg(x)$
解题思路:
原式:$f(x)=Ax^{2}g(x)+Bx^{2}+Cxg^{2}(x)+Dxg(x)$
$g(x)$ 为 $x$各位上的数字总和,而 $x$ 的数据范围是 $1$ 到 $1e6$,因此可直接算出 $g[x]$ 的最大值,即$g[999999]=54$。
所以$g(x)$只有$54$种不同的值,令 $g(x)=i$,当$i$确定后$f(x)$就成了关于$x$的二次函数,然后求它的最小值即可。
设$A=a*i+b$,$B=c*i^{2}+d*i$。
那么原式就可以转化成 $f(x)=A*x^{2}+B*x$
因此可先预处理满足$g(x)==i$的$x$值,然后枚举$i$从$1$到$54$,并去找最小值。
找最小值时应注意$A$的正负情况。
- 若 $A<=0$,则最小值出现在该二次函数的左右两侧
- 若 $A>0$,则需要用三分去找 $l$ 到 $r$ 中离对称轴最近的那个点(或直接找最小值)
Code
#include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int maxn=1e5+100; vector<int>v[60]; ll ans[maxn]; ll A,B; struct node{ int id; ll a,b,c,d,n; bool friend operator<(node x,node y){ return x.n<y.n; } }q[maxn]; ll get_g(int x){ ll ans=0; while(x){ ans+=x%10; x/=10; } return ans; } ll get_f(ll x){ return A*x*x+B*x; } ll solve(int u){ ll a,b,c,d,n; a=q[u].a,b=q[u].b,c=q[u].c,d=q[u].d,n=q[u].n; ll ans=6e18; for(int i=1;i<=54;i++){ if(v[i].size()==0){ continue; } A=a*i+b,B=c*i*i+d*i; if(A<=0){ ans=min(ans,get_f(v[i][0])); ans=min(ans,get_f(v[i][v[i].size()-1])); } else{ int l=0,r=v[i].size()-1; while(l<r){ int lmid=l+(r-l)/3,rmid=r-(r-l)/3; if(get_f(v[i][lmid])>get_f(v[i][rmid])){ l=lmid+1; } else{ r=rmid-1; } } ans=min(ans,get_f(v[i][l])); ans=min(ans,get_f(v[i][r])); } } return ans; } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ q[i].id=i; scanf("%lld%lld%lld%lld%lld",&q[i].a,&q[i].b,&q[i].c,&q[i].d,&q[i].n); } sort(q+1,q+n+1); int z=1; for(int i=1;i<=n;i++){ while(z<=q[i].n){ v[get_g(z)].push_back(z); z++; } ans[q[i].id]=solve(i); } for(int i=1;i<=n;i++){ cout<<ans[i]<<"\n"; } }