jzoj5678 【GDOI2018Day2模拟4.21】果树 (模型转化,覆盖线段树求在矩阵中点数)

Describe

NiroBC 姐姐是个活泼的少女,她十分喜欢爬树,而她家门口正好有一棵果树,正好满足了她爬树的需求。
这颗果树有N个节点,节点标号 1…N。每个节点长着一个果子,第i个节点上的果子颜色为 Ci 。
NiroBC姐姐每天都要爬树,每天都要选择一条有趣的路径 (u,v) 来爬。
一条路径被称作有趣的,当且仅当这条路径上的果子的颜色互不相同。
(u,v) 和 (v,u) 被视作同一条路径。特殊地,(i,i) 也被视作一条路径,这条路径只含 i 一个果子,显然是有趣的。
NiroBC姐姐想知道这颗树上有多少条有趣的路径。
n<=1e5

神奇但经典的模型转化

将一条路径转为二维平面上一点(u,v)
考虑路径不合法时的情况,假设其上有相同点a,b,不难发现当a,b不是祖先关系时,u,v分别在他们两个子树内,否则设a是b的祖先,p是ab路径上距离a最近的点,u,v其中一个在b子树内,一个不在q子树内。
子树在同一DFS序中,因此转换为矩阵覆盖问题,求有多少不在矩阵中点数。

有经典做法是,用一种叫覆盖线段树的东西我才不会告诉你是我自己取的名字,再加上扫描线。每次覆盖一段区间[a,b],就按线段树区间下发到每个点,打上tag.对于一个点,假如他tag!=0说明他仍被覆盖,否则就可从他儿子中获取被覆盖点数。

注意一个局限性就是,这种东西只能用于覆盖与撤销覆盖,必须先覆盖再撤销。 加一些特判应该是支持区间查询的。
写的很丑,抄袭需谨慎。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+10;
int c[N],to[N*2],nex[N*2],final[N],tot;
int f[N][18],dep[N];
int n,vis[N],has[N],L[N],R[N],stm,cnt;
int last[N],xd[N],lazy[4*N],sum[4*N];

long long ans;
void link(int x,int y) {
    to[++tot]=y,nex[tot]=final[x],final[x]=tot;
}
void dfs(int x,int fa) {
    L[x]=++stm;
    f[x][0]=fa; dep[x]=dep[fa]+1;
    for (int i=1; i<18; i++) f[x][i]=f[f[x][i-1]][i-1];
    for (int i=final[x]; i; i=nex[i]) {
        if (to[i]!=fa) dfs(to[i],x);
    }
    R[x]=stm;
}

int lca(int x,int y,int &z) {
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=17; i>=0; i--) if (dep[f[x][i]]>dep[y]) x=f[x][i];
    if (f[x][0]==y) {
        z=x; return f[x][0];
    } else if (dep[x]>dep[y]) x=f[x][0];

    for (int i=17; i>=0; i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}

struct line{
    int x,ly,ry,v;
} le[4*20*N];
bool cmp(line a,line b) {
    return a.x<b.x || a.x==b.x && a.v<b.v;
}
int ltot;
void make(int lx,int rx,int ly,int ry) {
    le[++ltot]=(line) {lx,ly,ry,1};
    le[++ltot]=(line) {rx+1,ly,ry,-1};
}
void make0(int lx,int rx,int ly,int ry) {
    make(lx,rx,ly,ry); make(ly,ry,lx,rx);
}
void update(int x,int sz) {
    if (lazy[x]!=0) sum[x]=sz; else sum[x]=sz==1?0:sum[x*2]+sum[x*2+1];
}
void change(int x,int l,int r,int tl,int tr,int v) {
    if (r<tl || l>tr) return;
    if (tl<=l && r<=tr) {
        lazy[x]+=v;
        update(x,r-l+1);
        return;
    }
    change(x*2,l,l+r>>1,tl,tr,v);
    change(x*2+1,(l+r>>1)+1,r,tl,tr,v);
    update(x,r-l+1);
}
int cnt1,cnt2;
int main() {
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    cin>>n;
    for (int i=1; i<=n; i++) scanf("%d",&c[i]);
    for (int i=1; i<n; i++) {
        int u,v; scanf("%d %d",&u,&v);
        link(u,v),link(v,u);
    }
    dep[1]=1; dfs(1,0);
    for (int i=1; i<=n; i++) {
        if (xd[c[i]]!=0) {
            for (int j=xd[c[i]]; j; j=last[j]) {
                int q=0,g=lca(i,j,q);
                if (q!=0) {
                    cnt2++;
                    int b=(dep[i]>dep[j])?i:j;
                    make0(L[b],R[b],1,L[q]-1);
                    make0(L[b],R[b],R[q]+1,n);
                } else make0(L[i],R[i],L[j],R[j]);
            }
            last[i]=xd[c[i]];
        }
        xd[c[i]]=i;
    }
    le[++ltot]=(line) {1,0,0,0};
    le[++ltot]=(line) {n+1,0,0,0};

    sort(le+1,le+1+ltot,cmp);
    int last=0;
    for (int i=1; i<=ltot; i++) {
        if (le[i].ly<=le[i].ry)
            change(1,1,n,le[i].ly,le[i].ry,le[i].v);

        if (i!=ltot && le[i+1].x!=le[i].x) {
            int jx=le[i+1].x-1;
            ans+=(long long)(jx-(le[i].x-1))*(n-sum[1]);
        }
    }
    cout<<(ans-n)/2+n<<endl;
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80031310