bzoj4194: Mat

思路:令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;
}



你可能感兴趣的:(bzoj4194: Mat)