bzoj2388(凸包+分块)

真心好难,代码不好写。

最后算是抄的clairs的代码。

考虑分块,每块维护两个标记 ts,td ts,td

那么对于块中一个位置 i i,它的实际值为 i×td+ts+vi i×td+ts+vi

修改的时候,对于整块,直接打标记,对于零散的暴力修改,然后重构凸壳,时间复杂度 O(n) O(n)

查询的时候在凸壳上二分即可,时间复杂度 O(nlogn) O(nlog⁡n)

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=120006;
const ll inf=1ll<<60;
inline ll read()
{
    ll ans,f=1;char ch;
    while((ch=getchar())<'0'||ch>'9') if (ch=='-')  f=-1;ans=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
    return ans*f;
}
int n,m,block,num,pos[N],st[N],en[N];
ll sum[N];
ll *v=sum;
double xielv(int i,int j)
{
    return (double)(sum[i]-sum[j])/(double)(j-i);
}
struct aa
{
    int q[605],tot;
    ll add,tag;
         
    ll query()
    {
        int mid,lt=2,rt=tot,ans=q[1];
        while (lt<=rt)
        {
            mid=(lt+rt)>>1;
            if (xielv(q[mid-1],q[mid])<tag) ans=q[mid],lt=mid+1;
            else rt=mid-1;
        }
        return sum[ans]+tag*ans+add;
    }//ok
}g[605];
 
void build(int x)
{
    int &tot=g[x].tot;tot=0;
    for (int i=st[x];i<=en[x];i++) 
    {
        while (tot>=2 && 
            xielv(g[x].q[tot-1],g[x].q[tot])>=xielv(g[x].q[tot],i)
        ) tot--;
        g[x].q[++tot]=i;
    }
}//ok

void init()
{
    n=read();
    ll x;
    block=sqrt(n);
    num=block+(block*block!=n);
    
    for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;
    for(int i=1;i<=n;i++)en[pos[i]]=i;
  	for(int i=n;i;i--)st[pos[i]]=i;
  	
    for (int i=1;i<=n;i++)
    {
        x=read();
        sum[i]=x+sum[i-1];
    }
    for (int i=1;i<=num;i++) build(i);
}//ok
inline void change(int x,int y,ll s,ll d){
  if(pos[x]==pos[y]){
    for(int i=x;i<=y;i++)v[i]+=s+d*i;
    build(pos[x]);
    return;
  }
  for(int i=pos[x]+1;i<pos[y];i++)g[i].add+=s,g[i].tag+=d;
  for(int i=en[pos[x]];i>=x;i--)v[i]+=s+d*i;
  build(pos[x]);
  for(int i=st[pos[y]];i<=y;i++)v[i]+=s+d*i;
  build(pos[y]);
}
inline void updata(){
	int x,y;ll z;
	x=read(),y=read(),z=read();
  change(x,y,z*(1-x),z);
  if(y<n)change(y+1,n,z*(y-x+1),0);
}
inline void up(ll&a,ll b){if(a<b)a=b;}
inline ll query(){
	int x=read(),y=read();
  ll t=-(1LL<<62);
  if(pos[x]==pos[y]){
    for(int i=x;i<=y;i++)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
    return t;
  }
  for(int i=pos[x]+1;i<pos[y];i++)up(t,g[i].query());
  for(int i=en[pos[x]];i>=x;i--)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
  for(int i=st[pos[y]];i<=y;i++)up(t,g[pos[i]].tag*i+g[pos[i]].add+v[i]);
  return t;
}
int main()
{
    init();
    int op;m=read();
    while (m--)
    {
        op=read();
        if (op==0) updata();
        else printf("%lld\n",query()); 
    }
    return 0;
}  

总结

1:注意变量等的命名,不要出现重复,例如ll类型和ll左端点不要重复。

2:这里用凸包,其实就是类似于斜率优化的式子,(就是设i<j,j优于i这种情况,推出的式子)也包括那些性质。

3:凸包就是在这些不能一维表示,然后须用二维,但是二维难以取区间最优值,就转化为凸包来解二维(有两个和i有关量的乘号)求。

4:此题思路须再整理!再分析!(今天太晚了。。)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值