UOJ Logo kczno1的博客

博客

bzoj 舌尖上的由乃

2017-05-14 10:22:59 By kczno1

我只想到了n根号(nlog)log的分块,感谢出题人教了我如何n根号log。

(假设分k块)

每个块内都排好序。

加的时候
整块的就打标记 O(k)
零散的,
考虑打标记后,没打标记的和打了标记的部分顺序都还是不变,
遍历一遍原来排好的顺序,就能得到这两个部分,之后归并一下就可以了。O(n/k)

求k大
先二分 转为check一个区间有多少数比一个数小。

按普通的想法
整块的二分 O(klog)
零散的暴力 O(n/k)
所以一次复杂度是O(klog^2+nlog/k)
k取根号(n/log),总复杂度就是O(n根号(nlog)log)

注意到,check零散的时间是O(nlog/k),这还可以优化。
首先,用遍历一遍原来顺序的方法 O(n/k)把两个零散的各自排序。
之后check的时候就可以二分了,O(log^2)
所以一次复杂度变成O(klog^2+n/k)
k取根号n/log,总复杂度就是O(n根号nlog)

还有一个方法,是跟值域有关的。

因为值域小了,就可以处理出每个块的前缀和

分块使得
1 每个块max-min<=x1*len
2 块的大小也不超过x2
3 块尽量大

如果不考虑2的话 块是n/x1个的
因为所有相邻两数的差的和是nlen的 因为n次区间加,每次加len
而相邻两块的max-min>x1
len
说明相邻两块所有相邻两数的差>max-min>x1len
所以块数<n
len/x1*len=n/x1

现在再把2考虑进来 相当于说一些大于x2的块要被拆掉
设大于x2的块的大小-x2的和是sum 那么每拆一次 sum-=x2
所以最多拆n/x2次

所以块数是n/x1+n/x2的。
因为所有块max-min的和<nlen,所以build的时间是nlen

check的时候
整块就可以O(1)
零散的暴力 就是x2
时间O(log*(n/x1+n/x2+x2))

加的时候整块打标记,O(n/x1+n/x2)
零散的暴力重算块的前缀和,O(x2+x1*len)

注意根号次加法后,块的max-min可能又大了根号*len,

这个时候要再重构一次分块。

时间(根号nlen)

x1,x2应该都要取根号

总时间O(n根号log+n根号len)

因为感觉第一种比较好写 我只写了第一种

#include<bits/stdc++.h>
using namespace std;

#define mid (l+r>>1)

const int ch_top=10000000;
char ch[ch_top],*now_r=ch,*now_w=ch-1;
int read()
{
    while(*now_r<'0')++now_r;
    int x=*now_r-'0';
    while(*++now_r>='0') x=x*10+*now_r-'0';
    return x; 
}
void write(int x)
{
    static char st[20];static short top;
    if(!x)*++now_w='0';
    else
    {
        for(;x;x/=10)st[++top]='0'+x%10;
        for(;top;--top)*++now_w=st[top];
    }
    *++now_w='\n';
}

const int N=100005,L=1500,K=N/L+5;
int f[N],w[N];vector<int>son[N];
typedef vector<int>::iterator ii;
int a[N],b[N];//b[i]是块排好序后的人 

int q[N],t,qa[N],la,qb[N],lb;
void merge_to(int *q)
{
    if(!lb) { memcpy(q+1,qa+1,la*sizeof(int));return ; }
    if(!la) { memcpy(q+1,qb+1,lb*sizeof(int));return ; }
    t=0;
    int ha=1,hb=1;
    for(;;)
    if(a[qa[ha]]<a[qb[hb]])
    {
        q[++t]=qa[ha];
        if(++ha>la) 
        {
            do
            {
                q[++t]=qb[hb];
            }while(++hb<=lb);
            break;
        }
    }
    else
    {
        q[++t]=qb[hb];
        if(++hb>lb) 
        {
            do
            {
                q[++t]=qa[ha];
            }while(++ha<=la);
            break;
        }
    }
}

int kl[K],kr[K],ad[K];
bool a_xiao(int x,int y)
{
    return a[x]<a[y];
}

void chmin(int &x,int y)
{
    if(x>y)x=y;
}
void chmax(int &x,int y)
{
    if(x<y)x=y;
}

int dfn[N],out[N],tot;
void dfs(int x,int fr,int now)
{
    now+=w[x];
    a[dfn[x]=++tot]=now;
    for(ii it=son[x].begin();it!=son[x].end();++it)
     dfs(*it,x,now);
    out[x]=tot;
}

int nl,nr;
int erfen(int *q,int t,int now)
{
    if(a[q[1]]>now)return 0;
    if(a[q[t]]<=now)return t;
    int l=1,r=t;
    while(l+1!=r)
    if(a[q[mid]]<=now) l=mid;
    else r=mid;
    return l;
}
int xiao_deng(int now)
{
   int ans=erfen(qa,la,now)+erfen(qb,lb,now);
   for(int k=nl+1;k<nr;++k) ans+=erfen(b+kl[k]-1,L/*kr[k]-kl[k]+1*/,now-ad[k]);
   return ans;
} 

void down(int k)
{
    int x=ad[k];
    if(!x)return ;
    for(int i=kl[k];i<=kr[k];++i) a[i]+=x;
    ad[k]=0;
}

int main()
{
    //freopen("1.in","r",stdin);freopen("3.out","w",stdout);
    ch[fread(ch,1,ch_top,stdin)]=0;
    int n=read(),m=read(),i,k;
    read();
    for(i=2;i<=n;++i)
    {
        son[f[i]=read()].push_back(i);
        w[i]=read();
    }

    dfs(1,0,0);

    tot=(n-1)/L;
    for(i=0;i<=tot+1;++i) {kl[i]=i*L+1;kr[i]=(i+1)*L;}
    kr[tot]=n;

    for(i=1;i<=n;++i)b[i]=i;
    for(k=0;k<=tot;++k)
        sort(b+kl[k],b+kr[k]+1,a_xiao);

    while(m--)
    {
        int type=read(),x=read();
        int ql=dfn[x],qr=out[x]; 
        //int ql=read(),qr=read();
        x=read();
        nl=(ql-1)/L;nr=(qr-1)/L;
        if(type==1)
        {
            int len=qr-ql+1;
            if(len<x) { *++now_w='-';*++now_w='1';*++now_w='\n';continue; }  
            down(nl);down(nr);
            if(len<=L*2)
            {
                down(nl+1);
                memcpy(q,a+ql,len*sizeof(int));
                nth_element(q,q+x-1,q+len);
                write(q[x-1]);
                continue;
            }
            else
            {
                la=0;
                for(i=kl[nl];i<=kr[nl];++i)
                if(b[i]>=ql) qa[++la]=b[i];
                lb=0;
                for(i=kl[nr];i<=kr[nr];++i)
                if(b[i]<=qr) qb[++lb]=b[i];
                //merge_to(q);

                int l=min(a[qa[1]],a[qb[1]]),r=max(a[qa[la]],a[qb[lb]]);
                for(k=nl+1;k<nr;++k) 
                {
                  chmin(l,a[b[kl[k]]]+ad[k]);
                  chmax(r,a[b[kr[k]]]+ad[k]); 
                }
                if(xiao_deng(l)>=x) { write(l);continue; }
                while(l+1!=r)
                {
                    if(xiao_deng(mid)>=x) r=mid;
                    else l=mid;
                }
                write(r);
            }
        }
        else
        {
            if(nl==nr)
            {
                for(i=ql;i<=qr;++i) a[i]+=x;
                la=lb=0;
                for(i=kl[nl];i<=kr[nl];++i) 
                if(b[i]>=ql&&b[i]<=qr) qa[++la]=b[i];
                else qb[++lb]=b[i];
                merge_to(b+kl[nl]-1);
                continue;
            }
            for(k=nl+1;k<nr;++k) ad[k]+=x;

            for(i=ql;i<=kr[nl];++i) a[i]+=x;
            la=0;lb=0;
            for(i=kl[nl];i<=kr[nl];++i)
            if(b[i]>=ql) qa[++la]=b[i];
            else qb[++lb]=b[i];
            merge_to(b+kl[nl]-1);

            for(i=kl[nr];i<=qr;++i) a[i]+=x;
            la=0;lb=0;
            for(i=kl[nr];i<=kr[nr];++i)
            if(b[i]<=qr) qa[++la]=b[i];
            else qb[++lb]=b[i];
            merge_to(b+kl[nr]-1);
        }
    }
    fwrite(ch,1,now_w-ch+1,stdout);
}

评论

OldDriverTree
这题好棒棒啊!

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。