UOJ295,大力分类讨论

我可是现在才去订正今年ZJOI的蒟蒻。
回想ZJOI2017Round2,我2小时过了T1大样例中k=2的点,然后半小时码完T2的20分暴力,接着2小时15分钟一直沉浸在子串l..r的后缀是i..n的幻觉中,最后15分钟暴力+卡常。
倘若我记得特判T1n=1的情况,又把T3的2小时拿去写T2的r=n的部分分(当时应该会做),或许就110分了?
但那都过去了。
吉司机是公布过官方题解的,但那比较难懂,我提几个细节。
1.lc[x]表示x到根的链上有多少个“左儿子”,等价于x到根的左链上右兄弟的个数(注意是兄弟,根节点没有兄弟)
2.ls[x]表示x到根的链上所有右兄弟的深度和
3.求答案时,和求两点间距离一样,先求出所有点的深度之和,再减去lca的深度之和。
记u是询问点,x是链的底端,y是链的顶端,v=lca(u,x)
如果dep[y]很显然,否则lca的深度之和等于v到y的点的深度之和加上x到v的点的个数乘v的深度
注意x到y不一定一直是左儿子或一直是右儿子,并且最多拐一次弯。
如果u在拐角处上面,那么v的一个儿子会给答案多贡献,要减掉,因为按照前述做法,会以为这个儿子a,是v的另一个儿子b

#include
#include
const int N=400010;
typedef long long ll;
inline char read() {
    static const int IN_LEN = 1000000;
    static char buf[IN_LEN], *s, *t;
    if (s == t) {
        t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
        if (s == t) return -1;
    }
    return *s++;
}
template<class T>
inline void read(T &x) {
    static bool iosig;
    static char c;
    for (iosig = false, c = read(); !isdigit(c); c = read()) {
        if (c == '-') iosig = true;
        if (c == -1) return;
    }
    /* 这里这么写的原因见下 */
    for (x = 0; isdigit(c); c = read())
        x = (x + (x << 2) << 1) + (c ^ '0');
    if (iosig) x = -x;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN], *oh = obuf;
inline void print(char c) {
    if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
    *oh++ = c;
}
template<class T>
inline void print(T x) {
    static int buf[30], cnt;
    if (x == 0) {
        print('0');
    } else {
        if (x < 0) print('-'), x = -x;
        for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
        while (cnt) print((char)buf[cnt--]);
    }
}
inline void flush() {
    fwrite(obuf, 1, oh - obuf, stdout);
}
int lson[N],rson[N],top[N],n,lc[N],rc[N],dep[N],m,u,l,r,rt,lp[N],rp[N],xb,id[N],dad[N],z;
bool ma[N];
ll ls[N],rs[N],ans;
void dfs(int&x,int fa,int l,int r,bool left){
    x=++xb,lp[x]=l,rp[x]=r,dep[x]=dep[fa]+1,dad[x]=fa;
    lc[x]=lc[fa]+left,ls[x]=ls[fa]+left*dep[x];
    rc[x]=rc[fa]+!left,rs[x]=rs[fa]+(!left)*dep[x];
    if(l==r)id[l]=x;
        else{
            int m;read(m);
            ma[x]=m-l+1>r-m;
            dfs(lson[x],x,l,m,1);
            dfs(rson[x],x,m+1,r,0);
        }
}
void dfs2(int x){
    if(ma[x])top[lson[x]]=top[x],top[rson[x]]=rson[x];
        else top[rson[x]]=top[x],top[lson[x]]=lson[x];
    if(lson[x])dfs2(lson[x]),dfs2(rson[x]);
}

inline int lca(register int u,register int v){
    register int t;
    while(top[u]!=top[v]){
        if(dep[top[u]]>dep[top[v]])t=u,u=v,v=t;
        v=dad[top[v]];
    }
    return dep[u]>dep[v]?v:u;
}
inline void lpath(register int x,register int y){//dep[x]>dep[y]
    ans+=1ll*(lc[x]-lc[y])*dep[u]+ls[x]-ls[y];
    register ll z=0;
    register int v=lca(u,x),t=v;
    if(dep[v]else z=v!=x && lp[rson[v]]<=lp[u] && rp[u]<=rp[rson[v]];
    z+=1ll*(lc[x]-lc[t])*dep[v]+ls[t]-ls[y]-(lc[t]-lc[y]);
    ans-=z<<1;
}
inline void rpath(register int x,register int y){
    ans+=1ll*(rc[x]-rc[y])*dep[u]+rs[x]-rs[y];
    register ll z=0;
    register int v=lca(u,x),t=v;
    if(dep[v]else z=v!=x && lp[lson[v]]<=lp[u] && rp[u]<=rp[lson[v]];
    z+=1ll*(rc[x]-rc[t])*dep[v]+rs[t]-rs[y]-(rc[t]-rc[y]);
    ans-=z<<1;
}
int main(){
    //freopen("segment.in","r",stdin);
    //freopen("2.txt","w",stdout);
    read(n);
    dfs(rt,0,1,n,0);
    top[1]=1;
    dfs2(rt);
    read(m);
    while(m--){
        read(u),read(l),read(r);
        if(l==1 && r==n){
            print(dep[u]-1);
            print('\n');
            continue;
        }
        ans=0;
        if(l==1)rpath(id[r+1],1);
            else if(r==n)lpath(id[l-1],1);
                    else{
                        z=lca(id[l-1],id[r+1]);
                        lpath(id[l-1],lson[z]);
                        rpath(id[r+1],rson[z]);
                    }
        print(ans),print('\n');
    }
    flush();
    return 0;
}

树剖lca加IO优化,似乎在uoj上是rk2?

你可能感兴趣的:(UOJ295,大力分类讨论)