bzoj4194: Mat

来源:互联网 发布:猎杀潜航手机版 java 编辑:程序博客网 时间:2024/05/17 00:52

思路:令K[i]=k[1]+k[2]+...+k[i],B[i]=b[1]+b[2]+..+b[i]

先取出每行第一个数,那么第i行第j列的元素就是A[i]+i*K[j]+B[j]

那么每行的答案ans[i]=min(i*K[j]+B[j])+A[i]

稍微变形:B[j]=i*(-K[j])+ans[i]-A[i]

把-K[j]看成横坐标,B[j]看成纵坐标,维护凸包搞一搞就可以了。愿意用单调队列用单调队列,愿意用单调栈的用单调栈,作死用splay的就用splay

考场上发神经写的splay维护,其实只要单调队列就可以了。splay代码太丑就不贴了。

代码:

#include<ctime>#include<cstdio>#include<cstring>#include<algorithm>#define ll long long#define min(a,b) (a<b?a:b)const int maxn=200010;const double eps=1e-10,inf=1e18;using namespace std;int n,m,q[maxn];ll A[maxn],K[maxn],B[maxn],cnt,top,ans;char ch;struct node{ll x,y;}p[maxn];double slope(int a,int b){    if (p[b].x-p[a].x<eps) return inf*(p[b].y-p[a].y);    return 1.0*(p[b].y-p[a].y)/(p[b].x-p[a].x);}bool cmp(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;} ll query(double k,int i){    int l=1,r=top;    if (!top) return (int)30077;    while (1){        int mid=(l+r)>>1;        double lk=slope(q[mid-1],q[mid]);        double rk=slope(q[mid],q[mid+1]);        if (lk<=eps+k&&rk+eps>=k) return p[q[mid]].y+1ll*i*p[q[mid]].x+A[i];        else if (k>rk) l=mid+1;else r=mid-1;    }} int main(){    scanf("%d%d",&n,&m);m--;    for (int i=1;i<=n;i++) scanf("%lld",&A[i]);    for (int i=1;i<=m;i++){        scanf("%lld%lld",&K[i],&B[i]);        p[i].x=p[i-1].x+K[i],p[i].y=p[i-1].y+B[i];    }    sort(p+1,p+1+m,cmp);    for (int i=1;i<=m;i++){        while (top>1&&slope(q[top-1],q[top])>slope(q[top],i)) --top;        q[++top]=i;    }    q[0]=0,q[top+1]=maxn-1,p[0]=(node){p[1].x-1,(ll)inf},p[maxn-1]=(node){p[q[top]].x+1,(ll)inf};    for (int i=1;i<=n;i++) printf("%lld\n",min(A[i],query(-1.0*i,i)));    return 0;}



0 0
原创粉丝点击