「ZJOI2019」语言 解题报告

「ZJOI2019」语言

3个\(\log\)做法比较简单,但是写起来还是有点麻烦的。

大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了。

好像这个可以被优化到两个\(\log\),算了,估计挺麻烦的。


一个\(\log\)的做法看起来还挺厉害的。

考虑钦定某个点算它的贡献,于是我们要算的是所有经过它的链的并的大小。

但是染色这个东西看起来就很不可搞,我们可以挖掘一下这个并的简单性质。

注意到,这个并是联通的,可以看做是一个生成子树,然后我们需要求这个子树的大小。

考虑一个类似建虚树的过程,我们把\(n\)个链拆成\(2n-1\)条链,即按\(dfs\)序进行排序,相邻两点构成链,我们按\(dfs\)序新加入一个点\(x\)时,新的链的贡献就是\(dep_x-dep_{lca(x,y)}\)\(y\)是上一个加入的点,这个可以画图体会一下。

但是一个一个加还是不太好维护,考虑我们可不可以合并两个子树的集合,显然,贡献和左边的最右端\(x\)以及右边的最左端\(y\)有关

然后我们发现需要讨论\(x,y\)是否在同一条链上,不在就很简单,两棵树没有交,随便算算就可以了。

如果在的话,我们发现有一部分是重叠的,算出这部分重叠可能需要求出左边的最浅点,为了方便,我们钦定每个集合的最浅点都是根(并加入根),然后在合并的时候可以发现,只需要减去\(dep_{lca(x,y)}\)就可以了,这对上面不在同一条链上仍然是适用的。

在合并到最后为了去掉根的贡献,我们维护一个点集的\(lca\)的深度,最后减去它就可以了。

这样的话,我们就可以用线段树维护这个子树的大小,并支持单点修改与查询。

对于链的修改,我们可以打差分tag,在相应的节点的线段树中加入与删除链的两个端点\(s,t\)

可以发现,为了继承儿子的信息,还需要一个线段树合并。


Code:

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <vector>
#define ll long long
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
template <class T>
void read(T &x)
{
    x=0;char c=gc();
    while(!isdigit(c)) c=gc();
    while(isdigit(c)) x=x*10+c-'0',c=gc(); 
}
const int N=1e5+10;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int n,m;
ll ans;
int dfn[N],ha[N],st[N<<1][20],Log[N<<1],fir[N],dep[N],par[N],dfsclock,dcnt;
void dfs(int now,int fa)
{
    dfn[now]=++dfsclock;
    ha[dfsclock]=now;
    st[++dcnt][0]=now;
    fir[now]=dcnt;
    dep[now]=dep[fa]+1;
    par[now]=fa;
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=fa)
        {
            dfs(v,now);
            st[++dcnt][0]=now;
        }
}
int LCA(int x,int y)
{
    if(!x||!y) return 0;
    x=fir[x],y=fir[y];
    if(x>y) std::swap(x,y);
    int d=Log[y+1-x];
    return dep[st[x][d]]<dep[st[y-(1<<d)+1][d]]?st[x][d]:st[y-(1<<d)+1][d];
}
struct node
{
    int a,b,d;
    node(){}
    node(int A,int B,int D){a=A,b=B,d=D;}
};
std::vector <node> yuu[N];
int ch[N*50][2],pot[N*50],lca[N*50],L[N*50],R[N*50],root[N],tot;
ll sum[N*50];
#define ls ch[now][0]
#define rs ch[now][1]
void updata(int now)
{
    sum[now]=sum[ls]+sum[rs]-dep[LCA(L[rs],R[ls])];
    L[now]=L[ls]?L[ls]:L[rs],R[now]=R[rs]?R[rs]:R[ls];
    if(lca[ls]&&lca[rs]) lca[now]=LCA(lca[ls],lca[rs]);
    else lca[now]=lca[ls]^lca[rs];	
}
void ins(int &now,int l,int r,int p,int d)
{
    if(!now) now=++tot;
    if(l==r)
    {
        pot[now]+=d;
        if(pot[now]) L[now]=R[now]=lca[now]=ha[l],sum[now]=dep[ha[l]];
        else L[now]=R[now]=lca[now]=sum[now]=0;
        return;
    }
    int mid=l+r>>1;
    if(p<=mid) ins(ls,l,mid,p,d);
    else ins(rs,mid+1,r,p,d);
    updata(now);
}
int Merge(int now,int las,int l,int r)
{
    if(!now||!las) return now^las;
    if(l==r)
    {
        pot[now]+=pot[las];
        if(pot[now]) L[now]=R[now]=lca[now]=ha[l],sum[now]=dep[ha[l]];
        else L[now]=R[now]=lca[now]=sum[now]=0;
        return now;
    }
    int mid=l+r>>1;
    ls=Merge(ls,ch[las][0],l,mid);
    rs=Merge(rs,ch[las][1],mid+1,r);
    updata(now);
    return now;
}
void dfs(int now)
{
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=par[now])
        {
            dfs(v);
            root[now]=Merge(root[now],root[v],1,n);
        }
    for(int i=0;i<yuu[now].size();i++)
    {
        int a=yuu[now][i].a,b=yuu[now][i].b,d=yuu[now][i].d;
        ins(root[now],1,n,dfn[a],d);
        ins(root[now],1,n,dfn[b],d);
    }
    //ll lastans=ans;
    ans+=sum[root[now]]-dep[lca[root[now]]];
    //printf("%d %lld\n",now,ans-lastans);
}
int main()
{
    read(n),read(m);
    for(int u,v,i=1;i<n;i++) read(u),read(v),add(u,v),add(v,u);
    dfs(1,0);
    for(int i=2;i<=dcnt;i++) Log[i]=Log[i>>1]+1;
    for(int j=1;j<=18;j++)
    {
        for(int i=1;i<=dcnt-(1<<j)+1;i++)
        {
            int x=st[i][j-1],y=st[i+(1<<j-1)][j-1];
            st[i][j]=dep[x]<dep[y]?x:y;
        }
    }
    for(int s,t,lca,i=1;i<=m;i++)
    {
        read(s),read(t);
        yuu[s].push_back(node(s,t,1));
        yuu[t].push_back(node(s,t,1));
        lca=LCA(s,t);
        yuu[lca].push_back(node(s,t,-1));
        yuu[par[lca]].push_back(node(s,t,-1));
    }
    dfs(1);
    printf("%lld\n",ans>>1);
    return 0;
}

2019.5.9

posted @ 2019-05-09 20:39  露迭月  阅读(307)  评论(0编辑  收藏  举报