GDOI2016模拟8.10查税

作为A镇的税务局长,您需要确保每个商业办公室都有足够的会计,并在某段时间内计算巡视范围内公司账户余额的最大值。

题目
A 镇刚刚成立,你被任命为税务局长,你的职责是保证镇上所有公司有足够多的会计。A 镇主街上有N 个商业办公室,从左到右依次编号为1 到N,一开始所有办公室都是空的,没有任何公司进驻,后面陆陆续续有公司进进出出。在某一个时间段,你会巡视一段连续编号的办公室,要求计算巡视的公司中账户余额最多的是多少。

一个公司的进驻用以下四个整数来描述:

T:表示进驻是哪一天,从A 镇成立那天算起,A 镇成立那天算第一天;

K:进驻的办公室的编号;

Z:该公司每天的赢利,如果亏损的话则为负值;

S:搬进来那天公司账户的余额。

如果在新公司搬进来之前第K 个办公室已经有公司进驻,那么这个公司会先搬出去,然后再把新公司搬进来,新搬进来的公司会在搬进来的第二天开始营业(赢利或亏损)。

你作为税务局长,会经常来巡视,用以下三个整数来描述:

T:巡视那天的编号;

A 和B:巡视第A 到第B 个编号范围的办公室。

由于巡视的时间是一天中很晚的时刻,所以所有公司已经完成当天的营业并且财务已经把当天的盈利更新到公司账户中。

你的任务就是对于每次巡视,找到账户余额最多的公司。

这题和cf上的91E差不多,考前2天做的,但没打出来,所以这题没多想了。

这题可以用分块做,对于一个快内,维护一个单调队列,维护块内直线方程的凸包,修改时,暴力重构,我们将快内的直线按斜率递增加入队列中,可以证明,当斜率递增的3条直线a,b,c,若a与c的交点在b上面,则b不会成为最优解,将其删去,由于暴力排序插入回超时,可以用链表维护,加入时只要移动到斜率恰好比它小的地方再删除即可。

由于询问是时间递增的,那么当凸包里A比B劣的时候,A不可能在后面比B优,删除即可。

就这样就可以在mn的复杂度内解决这题

贴代码(虽然数据有点水,我用快排+插入排序水过了,但希望大家打正解)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define MAXN 1000000000000
#define N 300001
#define M 400
using namespace std;
int n,m,s,sum;
long long d[M][M][2],e[N][2];
int g[M],a[N+N][5],b[N],t[N],l[M],r[M],q[N][5];
int did(int x){
    return (x%s>0)+x/s;
}
void ins(int x,int i){
    a[++sum][0]=q[i][3];
    a[sum][1]=q[i][4];
    a[sum][2]=q[i][1];
    a[sum][3]=q[i][2];
    a[sum][4]=g[x];
    g[x]=sum;
}
void ins1(int x,int i){
    a[++sum][0]=a[i][0];
    a[sum][1]=a[i][1];
    a[sum][2]=a[i][2];
    a[sum][3]=a[i][3];
    a[sum][4]=g[x];
    g[x]=sum;
}
void init(){
    static int x;
    scanf("%d %d",&n,&m);
    s=sqrt(n)+1;
    for (int i=1;i<=m;i++){
        scanf("%d %d %d %d",&q[i][0],&q[i][1],&q[i][2],&q[i][3]);
        if (q[i][0]==1){
            scanf("%d",&q[i][4]);
            ins(did(q[i][2]),i);
        }
    }
}
bool cmp(int x,int y){
    return a[x][0]<a[y][0];
}
void pre(){
    static int x;
    for (int i=1;i<=s;i++){
        x=0;
        for (int j=g[i];j;j=a[j][4])
            b[++x]=j;
        sort(b+1,b+x+1,cmp);
        g[i]=0;
        for (int j=x;j;j--)
            ins1(i,b[j]);
    }
}
bool jian(long long a1,long long b1,long long a2,long long b2,long long a3,long long b3){
    return (long long)(b1-b2)*(a1-a3)<=(long long)(b3-b1)*(a2-a1);
}
void ins(int x,int a,long long b){
    r[x]++;
    while (r[x]>=3&&jian(a,b,d[x][r[x]-2][0],d[x][r[x]-2][1],d[x][r[x]-1][0],d[x][r[x]-1][1]))r[x]--;
    d[x][r[x]][0]=a,d[x][r[x]][1]=b;
}
void rebuild(int x){
    static int y;
    l[x]=1,r[x]=0;
    for (int i=g[x];i;i=a[i][4])
        if (t[a[i][3]]==a[i][2])
            ins(x,a[i][0],a[i][1]-(long long)a[i][0]*a[i][2]);
}
long long find(long long t,int x){
    if (!l[x])return -MAXN;
    while (l[x]<r[x]&&d[x][l[x]][0]*t+d[x][l[x]][1]<=d[x][l[x]+1][0]*t+d[x][l[x]+1][1])l[x]++;
    return d[x][l[x]][0]*t+d[x][l[x]][1];
}
void work(){
    static int x,y;
    static long long ans;
    for (int i=1;i<=m;i++)
        if (q[i][0]==1)e[q[i][2]][0]=q[i][3],e[q[i][2]][1]=q[i][4]-(long long)q[i][3]*q[i][1],t[q[i][2]]=q[i][1],rebuild(did(q[i][2]));
        else{
            if (q[i][2]>q[i][3])swap(q[i][2],q[i][3]);
            x=did(q[i][2]),y=did(q[i][3]);
            ans=-MAXN;
            for (int j=x+1;j<y;j++)
                ans=max(ans,find(q[i][1],j));
            if (x==y){
                for (int j=q[i][2];j<=q[i][3];j++)
                    if (t[j])
                        ans=max(ans,(long long)e[j][0]*q[i][1]+e[j][1]);
            }else{
                for (int j=min(x*s,n);j>=q[i][2];j--)
                    if (t[j])
                        ans=max(ans,(long long)e[j][0]*q[i][1]+e[j][1]);
                for (int j=(y-1)*s+1;j<=q[i][3];j++)
                    if (t[j])
                        ans=max(ans,(long long)e[j][0]*q[i][1]+e[j][1]);
            }
            if (ans==-MAXN)printf("nema\n");
            else
            printf("%lld\n",ans);
        }
}
int main(){
    init();
    pre();
    work();
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值