[BZOJ2957] building reconstruction

Posted by ndondo on Thu, 30 Apr 2020 16:38:20 +0200

It is obvious that the point can be regarded as the slope, and then the longest ascending subsequence from the beginning can be calculated, which supports single point modification.
Block approach (no brain):
Consider dividing the sequence into B blocks, each of which maintains the longest ascending subsequence from the beginning, and records with a stack. Each query is equivalent to scanning all the blocks once. In the current block stack, it is divided into the first position that is larger than the maximum value of the previous block, and then its contribution plus the number of elements to the top of the stack, complexity O(nlogBB). Modify the direct violence refactoring block, complexity O(B). Total complexity O(n * (nlogBB+B)). It is reasonable that B=nlogn − − − √ is better, but the measured B=400 is the fastest...
Line tree method (thinking):
It is found that modifying a point only affects the later part of it. We remember that max is the maximum value of the current interval, ans is only the answer of the current interval, and we consider how to update, that is, how to merge the information of two subtrees.
Considering that the current ansx should be anslson, plus the larger part of anslson than maxlson, we need to implement a function cal(x,d) to represent the length of the longest ascending subsequence with a starting value greater than D in X. The discussion can be divided into two situations:
If lsonmax ≤ d, we can recurse right subtree directly without considering left subtree.
If lsonmax > D, the answer is cal(lson,d)+cal(rson,maxlson), but the second half is obviously ansx − anslson, so you only need to recurse the left subtree.
Because each update is also O(logn), the total complexity is O(nlog2n).
Block code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ID(x) (((x)-1)/B+1)
#define L(x) ((x-1)*B+1)
#define R(x) min(n,x*B)
#define ll long long
using namespace std;
const int B=400;
const int maxn=100010;
int n,m,h[maxn];
bool cmp(int p,int q){return (!p&&h[q])||((ll)h[p]*q<(ll)h[q]*p);}
struct block
{
    int top,l,r,st[B+10];
    void init(int x){l=L(x);r=R(x);}
    void rebuild(){top=0;for(int i=l;i<=r;i++)if(cmp(st[top],i)) st[++top]=i;}
    int qry(int &last){int x=upper_bound(st+1,st+top+1,last,cmp)-st;if(x<=top) last=st[top];return top-x+1;}
}a[ID(maxn)+10];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=ID(n);i++) a[i].init(i);
    for(int i=1,x;i<=m;i++)
    {
        scanf("%d",&x);scanf("%d",&h[x]);a[ID(x)].rebuild();
        int last=0,ans=0;for(int i=1;i<=ID(n);i++)ans+=a[i].qry(last);
        printf("%d\n",ans);         
    }
    return 0;
}

Line tree code:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100010;
int n,m;
struct tree
{
    tree *ls,*rs;int s;double mx;
    tree(){ls=rs=NULL;}
    void build(int l,int r)
    {
        mx=0;s=0;
        if(l==r) return ;
        int mid=l+r>>1;
        (ls=new tree)->build(l,mid);
        (rs=new tree)->build(mid+1,r);
    }
    int cal(int l,int r,double dw)
    {
        if(l==r) return mx>dw;
        int mid=l+r>>1;
        if(ls->mx<=dw) return rs->cal(mid+1,r,dw);
        else return s-ls->s+ls->cal(l,mid,dw);        
    }
    void mdf(int l,int r,int pl,double d)
    {
        if(l==r){mx=d;s=1;return;}
        int mid=l+r>>1;
        if(pl<=mid) ls->mdf(l,mid,pl,d);
        else rs->mdf(mid+1,r,pl,d);
        mx=max(ls->mx,rs->mx);
        s=ls->s+rs->cal(mid+1,r,ls->mx);
    }
}*xtr;
int main()
{
    scanf("%d%d",&n,&m);
    (xtr=new tree)->build(1,n);
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        xtr->mdf(1,n,x,(double)y/x);
        printf("%d\n",xtr->s);
    }
    return 0;
}