bzoj2388(凸包+分塊)

真心好難,代碼不好寫。

最後算是抄的clairs的代碼。

考慮分塊,每塊維護兩個標記ts,tdts,td

那麼對於塊中一個位置ii,它的實際值爲i×td+ts+vii×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:此題思路須再整理!再分析!(今天太晚了。。)

 

發佈了331 篇原創文章 · 獲贊 17 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章