被论文洗脑(徐明宽<<非常规大小分块算法初探>>)

今天闲来无事,无题可做(因为太菜了开的题都不会做)

于是打开了2017IOI国家候选队的论文,徐明宽的<<非常规大小分块算法初探>>

http://www.noi.cn/noi-news/noi/786-ctsc2017

然后略过了前面一些看不太懂的东西.

看到了method of four russians这个lca算法

预处理O(n),查询O(1),还是在线的

看起来很牛逼,于是就仔细理解了一下

又上网找了找资料,结果发现网上没什么资料

于是就根据论文自己打了一打.

然后就不好了,打了一个下午.

 

原文对做法讲的已经十分清楚了

首先dfs,处理出dfs序(一个点出现多次的那种)

然后转化为正负1RMQ问题(因为相邻两点深度差最多为1)

然后正负1RMQ问题可以分块解决

块之间用普通RMQ倍增法维护

一个块内直接用一种难以描述的方法维护

具体来说是这样

设一个块内有s个元素,分别为a[0]-a[s-1]

先差分,b[i]=a[i+1]-a[i]

i属于[0,s-2]

然后所有的块就可被归为2^(s-1)类,因为b[i]不是-1,就是1(0?不存在的,一条边两边点的深度不可能相等)

每一类处理出在一定范围内最小值的位置

然后就好了

查询时乱搞一波

具体来说就是包含块算块,注意边界,无块就用维护块内信息的数组回答.

(话说这方法考试时真能打吗?直接被续光时间)

至于这个算法的常数,详见论文.

 

例题

洛谷P3379 【模板】最近公共祖先(LCA)

上代码

//method of four russians
//prework O(n) ask O(1)
#include<bits/stdc++.h>
using namespace std;
const int N=500010,M=500010,KK=250010,P=19,S_2=256,S=9;
int b[N<<1],ne[N<<1],fi[N],k,n,m,root,s;
inline void read(int &x){
    x=0;  char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void add(int x,int y){
    b[++k]=y; ne[k]=fi[x]; fi[x]=k;
    b[++k]=x; ne[k]=fi[y]; fi[y]=k; 
}
void in(){
    read(n); read(m); read(root);
    for (int i=1; i<n; ++i){
        int x,y; read(x); read(y);
        add(x,y);
    }
    s=log2(n)/2;
    if (!s) s=1;
//    cerr<<"in"<<s<<endl;
}
int a[(N<<1)+10],deep[N],la[N],len;
void dfs(int x){
    la[x]=len; a[len++]=x; 
    for (int j=fi[x]; j; j=ne[j])
    if (!deep[b[j]]){
        deep[b[j]]=deep[x]+1;
        dfs(b[j]);
        la[x]=len; a[len++]=x;
    }
}
int f[P][KK],lg[KK],minn[S_2][S][S],dep[S],re[KK];
void prework(){
    deep[0]=INT_MAX; deep[root]=1; dfs(root);    
//    for (int i=0; i<len; i++) cerr<<a[i]<<" "; puts("");
//    for (int i=0; i<len; i++) cerr<<deep[a[i]]<<" "; puts("");
    /*-----------------------------------------------------*/    
    //S numbers,s-1 interval,less than 2^(s-1) 
    for (int i=0; i<(1<<(s-1)); i++)
        for (int j=0; j<s; j++){//from 0
            minn[i][j][j]=j; dep[j]=0;
            for (int k=j+1; k<s; k++){
                if ((i>>(k-1))&1) dep[k]=dep[k-1]+1; else dep[k]=dep[k-1]-1;
                if (dep[k]<=dep[minn[i][j][k-1]]) minn[i][j][k]=k; else minn[i][j][k]=minn[i][j][k-1];
//                cerr<<i<<" "<<j<<" "<<k<<" "<<dep[k]<<" "<<minn[i][j][k]<<endl; system("pause");
            }
        }
    for (int i=0; i<=(len-1)/s; i++){
        int l=i*s,t=0;
        for (int j=0; j<s-1; j++) if (deep[a[l+1+j]]-deep[a[l+j]]!=-1) t|=1<<j;
        re[i]=t;
//        cerr<<i<<" "<<re[i]<<endl;
    }
    /*----------------------------------------------------*/
    int kk=(len-1)/s,p=(int)log2(kk+1)+1;
    lg[1]=0; for (int i=2; i<=kk; i++) lg[i]=lg[i>>1]+1;
    /*----------------------------------------------------*/
    for (int i=0; i<len; i++) if (deep[f[0][i/s]]>deep[a[i]]) f[0][i/s]=a[i];
    for (int j=1; j<=p; j++)
    for (int i=0; i<=kk; i++)
    if (i+(1<<(j-1))<=kk){
        int u=f[j-1][i],v=f[j-1][i+(1<<(j-1))];
        if (deep[u]<deep[v]) f[j][i]=u; else f[j][i]=v;
    }else break;
    /*-----------------------------------------------------*/
}
int ask(int l,int r){
    if (l>r) l^=r^=l^=r;
    int sl=l/s,sr=r/s,ll=l-sl*s,rr=r-sr*s;
    if (sl!=sr){
        int res=0,u,v;
        if (sl+1<=sr-1){
            int j=lg[sr-sl-1];
            u=f[j][sl+1]; v=f[j][sr-(1<<j)];//from sl+1 to sr-1
            if (deep[u]<deep[v]) res=u; else res=v;
        }
        u=a[r-rr+minn[re[sr]][0][rr]];
        if (deep[u]<deep[res]) res=u;
        v=a[l-ll+minn[re[sl]][ll][s-1]];
        if (deep[v]<deep[res]) res=v;
        return res;
    }
    else return a[l-ll+minn[re[sl]][ll][rr]];
}
void ans_the(){
    while (m--){
        int x,y; scanf("%d%d",&x,&y);
        printf("%d\n",ask(la[x],la[y]));
    }
}
int main(){
    in();
    prework();
    ans_the();
}

 

posted @ 2018-02-09 16:05  Yuhuger  阅读(1291)  评论(0编辑  收藏  举报