[ZJOI2019]语言

[ZJOI2019]语言 

法一: O(nlog^3)树剖+矩形面积并

法二:O(nlog^2)

一个点能到达的点是所有经过它的链的链并的大小

给出m个链求链并:

类似虚树

dfn序排序,ans+=dep[mem[i]]-dep[lca(mem[i],mem[i-1])]

每个边一定会统计到。

 

维护?

每个链树剖拆成logn份,线段树分治+线段树

下标都是dfn序

dfn相邻的贡献答案

 

法三:O(nlogn)

不再从dfn序列孤立地考虑树链

从树上直接考虑

经过一个点x的链一定一端在x的子树,启发我们线段树合并!

下标还是dfn序

树上差分打标记,8个标记。

lca可以O(1)求

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=1e5+5;
int n,m;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}

int dfn[N],df,fdfn[N];
int st[N],num;
int dep[N];
int mem[2*N];
int fa[N];
void dfs(int x,int d){
    dep[x]=d;dfn[x]=++df;
    fdfn[df]=x;
    st[x]=++num;mem[num]=x;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        fa[y]=x;
        dfs(y,d+1);
        mem[++num]=x;
    }
}
int anc[2*N][20];
int lg[2*N];
int lca(int x,int y){
    if(!x||!y) return 0;
    if(st[x]>st[y]) swap(x,y);
    x=st[x],y=st[y];
    int len=lg[y-x+1];
    int p1=anc[x][len],p2=anc[y-(1<<len)+1][len];
    return dep[p1]<dep[p2]?p1:p2;
}
int con(int x,int y){
    if(!x||!y) return 0;
    return dep[fdfn[y]]-dep[lca(fdfn[x],fdfn[y])];
}
struct tr{
    int ls,rs;
    int le,ri;
    int tag;
    int ans;
}t[N*160];
#define mid ((l+r)>>1)
int tot;
void pushup(int x){
    t[x].ans=t[t[x].ls].ans+t[t[x].rs].ans+con(t[t[x].ls].ri,t[t[x].rs].le);
    t[x].ri=t[t[x].rs].ri?t[t[x].rs].ri:t[t[x].ls].ri;
    t[x].le=t[t[x].ls].le?t[t[x].ls].le:t[t[x].rs].le;
}
int rt[N];
void upda(int &x,int l,int r,int p,int c){
    if(!x) x=++tot;
    if(l==r){
        t[x].tag+=c;
        if(t[x].tag>0) t[x].le=t[x].ri=l;
        else t[x].le=t[x].ri=0;
        return;
    }
    if(p<=mid) upda(t[x].ls,l,mid,p,c);
    else upda(t[x].rs,mid+1,r,p,c);
    pushup(x);
}
int merge(int x,int y,int l,int r){
    if(!x||!y) return x+y;
    if(l==r){
        t[x].tag+=t[y].tag;
        if(t[x].tag>0) t[x].le=t[x].ri=l;
        else t[x].le=t[x].ri=0;
        return x;
    }
    t[x].ls=merge(t[x].ls,t[y].ls,l,mid);
    t[x].rs=merge(t[x].rs,t[y].rs,mid+1,r);
    pushup(x);
    return x;
}
ll ans;
void sol(int x){
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        sol(y);
        rt[x]=merge(rt[x],rt[y],1,n);
    }
    int now=t[rt[x]].ans+dep[fdfn[t[rt[x]].le]]-dep[fa[lca(fdfn[t[rt[x]].le],fdfn[t[rt[x]].ri])]];
    // cout<<" sol "<<x<<" now "<<now<<" ans "<<t[rt[x]].ans<<" "<<endl;
    if(now) ans+=now-1;
}
int main(){
    rd(n);rd(m);
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
    }
    dfs(1,1);
    for(reg i=1;i<=num;++i) {
        anc[i][0]=mem[i];
        lg[i]=(i>>(lg[i-1]+1)?lg[i-1]+1:lg[i-1]);
    }
    for(reg j=1;j<=17;++j){
        for(reg i=1;i+(1<<j)-1<=num;++i){
            anc[i][j]=dep[anc[i][j-1]]<dep[anc[i+(1<<(j-1))][j-1]]?anc[i][j-1]:anc[i+(1<<(j-1))][j-1];
        }
    } 
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);
        int z=lca(x,y);
        // cout<<" zz "<<z<<endl;
        upda(rt[x],1,n,dfn[x],1);
        upda(rt[x],1,n,dfn[y],1);
        upda(rt[y],1,n,dfn[x],1);
        upda(rt[y],1,n,dfn[y],1);
        upda(rt[z],1,n,dfn[x],-1);
        upda(rt[z],1,n,dfn[y],-1);
        if(fa[z]){
            upda(rt[fa[z]],1,n,dfn[x],-1);
            upda(rt[fa[z]],1,n,dfn[y],-1);
        }
    }
    sol(1);
    // cout<<" 00 "<<rt[0]<<" "<<t[0].ans<<" "<<t[0].ls<<" "<<t[0].rs<<" "<<t[0].le<<" "<<t[0].ri<<" "<<t[0].tag<<endl;
    ans/=2;
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

 

上一篇:[ZJOI2019]线段树


下一篇:[ZJOI2019]Minimax搜索(线段树+动态DP+树剖)