题目
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;
}
作为A镇的税务局长,您需要确保每个商业办公室都有足够的会计,并在某段时间内计算巡视范围内公司账户余额的最大值。
361

被折叠的 条评论
为什么被折叠?



