题面:https://www.cnblogs.com/Juve/articles/11534880.html
A:
T可以写成如下形式:$T=b^k*S+m*a$,
其中$m=sumlimits_{i=1}^{k}p_i*b^i$
然后k最多64,所以枚举即可
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #define int long long #define re register using namespace std; int s,t,a,b,ans=0x7fffffff,res,tot=0,n=1; signed main(){ scanf("%lld%lld%lld%lld",&s,&t,&a,&b); while(t-s*n>=0){ re int p=t-s*n; if(p%a==0){ p/=a; res=0; for(re int i=tot;i>=0;--i){ int q=pow(b,i); res+=p/q; p%=q; } ans=min(res+tot,ans); } n*=b; ++tot; } printf("%lld ",ans); return 0; }
C:
有一个贪心策略
对于每一个点,我们找能加热到它的加热器中右端点最大的一个,然后加热
如果没有符合的就用特殊加热器
如果扫到当前点它已经加热超过p了就跳过
然后有一个部分分算法
如果p很小,我们可以枚举特殊加热器的使用次数,提前给他们加热,然后用贪心策略解决
之后我们优化贪心
我们发现预先枚举的使用特殊加热器的次数所得到的答案具有单谷性质,所以我们可以三分
修改操作用差分和树状数组即可
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define int long long #define re register using namespace std; const int MAXN=1e5+5; int n,m,t,p[MAXN],maxx=0,ans,g[MAXN],c[MAXN],l,r; struct node{ int l,r; friend bool operator < (node a,node b){ return a.l==b.l?a.r<b.r:a.l<b.l; } }pos[MAXN]; int lowbit(int x){ return x&-x; } int update(int x,int val){ while(x<=n){ c[x]+=val; x+=lowbit(x); } } int query(int x){ int res=0; while(x>0){ res+=c[x]; x-=lowbit(x); } return res; } int check(int k){ int res=k*t; for(int i=1;i<=n;++i) c[i]=0; for(int i=1;i<=n;++i){ int q=query(i); if(q+k>=p[i]) continue; if(g[i]==0||g[i]>n){ return -0x7fffffffffffffff; }else{ int tim=p[i]-q-k; res+=tim; update(i,tim); update(g[i]+1,-tim); } } return -res; } signed main(){ scanf("%lld%lld%lld",&n,&m,&t); for(int i=1;i<=n;++i){ scanf("%lld",&p[i]); maxx=max(maxx,p[i]); } for(int i=1;i<=m;++i) scanf("%lld%lld",&pos[i].l,&pos[i].r); sort(pos+1,pos+m+1); int j=1,mx=0; for(int i=1;i<=n;++i){ if(mx<i) mx=0; while(j<=m&&pos[j].l<=i) mx=max(mx,pos[j++].r); g[i]=mx; } l=0,r=maxx; while(r-l>2){ int lmid=(l+r)>>1; int rmid=lmid+1; if(check(lmid)<=check(rmid)) l=lmid; else r=rmid; } ans=min(min(-check(l),-check(r)),-check(l+1)); printf("%lld ",ans); return 0; }
F:
30分dp:设f[i][j]表示前i个操作,指针一个在p[i],一个在j的最小代价
转移:
$f[i][j]=min{f[i-1][j]+abs(pos[i]-pos[i-1])}$
$f[i][pos[i-1]]=min(f[i][pos[i-1]],f[i-1][j]+abs(j-pos[i]))$
然后考虑优化
第一个式子我们可以用线段树的区间加
第二个式子我们把abs拆开,用线段树维护f[i][j]+j和f[i][j]-j的最小值
具体操作:先用线段树分别找f[i][j]+j和f[i][j]-j的最小值,比较f[i][j]+j-pos[i]和f[i][j]-j+pos[i],
然后将整个区间加上abs(pos[i]-pos[i-1]),最后将pos[i-1]处的权值修改为上面的最小值
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define int long long #define re register using namespace std; const int MAXN=1e5+5; int n,q,a,b,pos[MAXN]; struct node{ int val,min1,min2,l,r,laz; }tr[MAXN<<2]; void pushup(int k){ tr[k].val=min(tr[k<<1].val,tr[k<<1|1].val); tr[k].min1=min(tr[k<<1].min1,tr[k<<1|1].min1); tr[k].min2=min(tr[k<<1].min2,tr[k<<1|1].min2); } void down(int k){ tr[k<<1].val+=tr[k].laz; tr[k<<1].min1+=tr[k].laz; tr[k<<1].min2+=tr[k].laz; tr[k<<1].laz+=tr[k].laz; tr[k<<1|1].val+=tr[k].laz; tr[k<<1|1].min1+=tr[k].laz; tr[k<<1|1].min2+=tr[k].laz; tr[k<<1|1].laz+=tr[k].laz; tr[k].laz=0; } void build(int k,int l,int r){ tr[k].l=l,tr[k].r=r; tr[k].laz=0; tr[k].val=tr[k].min1=tr[k].min2=0x7fffffff; if(l==r){ tr[k].val=tr[k].min1=tr[k].min2=0x7fffffff; if(l==a){ tr[k].val=abs(pos[1]-b); tr[k].min1=tr[k].val-l; tr[k].min2=tr[k].val+l; } if(l==b){ tr[k].val=abs(pos[1]-a); tr[k].min1=tr[k].val-l; tr[k].min2=tr[k].val+l; } return ; } int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); pushup(k); } void update(int k,int opt,int val){ int l=tr[k].l,r=tr[k].r; if(l==r){ tr[k].val=val; tr[k].min1=tr[k].val-tr[k].l; tr[k].min2=tr[k].val+tr[k].l; return ; } if(tr[k].laz) down(k); int mid=(l+r)>>1; if(opt<=mid) update(k<<1,opt,val); else update(k<<1|1,opt,val); pushup(k); } void change(int k,int opl,int opr,int val){ int l=tr[k].l,r=tr[k].r; if(opl<=l&&r<=opr){ tr[k].val+=val; tr[k].min1+=val; tr[k].min2+=val; tr[k].laz+=val; return ; } if(tr[k].laz) down(k); int mid=(l+r)>>1; if(opl<=mid) change(k<<1,opl,opr,val); if(opr>mid) change(k<<1|1,opl,opr,val); pushup(k); } int query1(int k,int opl,int opr){ int l=tr[k].l,r=tr[k].r; if(opl<=l&&r<=opr){ return tr[k].min1; } if(tr[k].laz) down(k); int mid=(l+r)>>1,res=0x7fffffff; if(opl<=mid) res=min(res,query1(k<<1,opl,opr)); if(opr>mid) res=min(res,query1(k<<1|1,opl,opr)); return res; } int query2(int k,int opl,int opr){ int l=tr[k].l,r=tr[k].r; if(opl<=l&&r<=opr){ return tr[k].min2; } if(tr[k].laz) down(k); int mid=(l+r)>>1,res=0x7fffffff; if(opl<=mid) res=min(res,query2(k<<1,opl,opr)); if(opr>mid) res=min(res,query2(k<<1|1,opl,opr)); return res; } signed main(){ scanf("%lld%lld%lld%lld",&n,&q,&a,&b); for(re int i=1;i<=q;++i) scanf("%lld",&pos[i]); build(1,1,n); for(int i=2;i<=q;++i){ int res=min(query1(1,1,pos[i])+pos[i],query2(1,pos[i],n)-pos[i]); change(1,1,n,abs(pos[i]-pos[i-1])); update(1,pos[i-1],res); } printf("%lld ",tr[1].val); return 0; }