题解 P2664 【树上游戏】

__ZJ

2018-03-10 14:44:22

Solution

### 扫描线,线段树维护区间覆盖 (画风诡异,真的没有点分治,没有虚树。。。) **●简要版:** 对每种颜色分别考虑, 每个点会贡献的S(i,j)形成平面上的若干矩形。 然后用扫描线+线段树维护区间覆盖去求得: 只考虑该颜色时的每个sum[u](准确说是sum[u]的差分数组) 把每种颜色得到的sum[u]加起来就是答案了。 **●详细版:** 事先dfs一遍这颗树,对每个点u记录下它的dfs序号。 然后维护出be[u](begin)表示u的子树内最小的dfs序号(显然就是它自己的序号啦) 以及en[u](end)表示u的子树内最大的dfs序号(也就是u子树里最后一个被遍历到的点的序号) 考虑树上的某一个点u,它的颜色为c,假设其它点都是没有颜色的, 那么来考虑一下,这个点会为哪些S(i,j)贡献1的值 显然可以分为两类情况: 1).i为u外面的点,j为x子树内的点。 (或者j为u外面的点,i为u子树内的点) 2).i,j分别为x的不同儿子的子树内的点。 (这样的话,i,j的lca为u嘛)。 现在我们赋予二元组(i,j)几何意义:对应着坐标平面上的一个点(i,j), 有了二维平面,我们在把之前的两类情况反映到平面上:(建议画图理解) 1).i,j一个为u外面的点,另一个为u子树内的点。 子情况(1):i=1~be[u],j=be[u]~en[u] 那么这就对应着(1~be[u],be[u]~en[u])这样一个矩形, 子情况(2,类似上面,只是把i,j交换):i=be[u]~en[u],j=1~be[u] 那么这就对应着(be[u]~en[x],1~be[u])这样一个矩形, 子情况(3): i=be[u]~en[u],j=en[u]~N 那么这就对应着(be[u]~en[u],en[u]~N)这样一个矩形, 子情况(4,类似上面,只是把i,j交换):i=en[u]~N,j=be[u]~en[u] 那么这就对应着(en[u]~N,be[u]~en[u])这样一个矩形, 2).i,j分别为x的不同儿子的子树内的点。 这里需要枚举u的每个儿子v: 子情况(1):i=be[u]~(en[v]-1),j=be[v]~en[v] 那么这就对应着(be[u]~(en[v]-1),be[v]~en[v])这样一个矩形, 子情况(2,类似上面,只是把i,j交换):i=be[v]~en[v],j=be[u]~(en[v]-1) 那么这就对应着(be[v]~en[v],be[u]~(en[v]-1))这样一个矩形, 子情况(3):i=be[v]~en[v],j=(en[v]+1)~en[u] 那么这就对应着(be[v]~en[v],(en[v]+1)~en[u])这样一个矩形, 子情况(4,类似上面,只是把i,j交换):i=(en[v]+1)~en[u],j=be[v]~en[v] 那么这就对应着((en[v]+1)~en[u],be[v]~en[v])这样一个矩形, 也就是说,我们把上面这些矩形覆盖在平面上, 如果发现(i,j)这个点被覆盖的话,就表明,i到j的路径上有这么一个颜色。 由于一条路径上的同一个颜色只能计算一次。 所以把相同颜色的点归在一起,然后一种颜色一种颜色去做。 把当前颜色的点产生的矩形全部去覆盖平面, 然后用扫描线+线段树维护区间覆盖去得到每个x位置上y被覆盖的长度y_cover, y_cover的含义就是,只考虑当前颜色时,sum[x]的值 (当然不用逐个求出每个x位置上y的覆盖长度,这里采用一个数组去记录sum[x]的差分,最后再求一遍前缀就好了) **●时间复杂度分析:** 显然影响时间复杂度的关键在维护若干矩形的覆盖上面。 对于每个矩形,会存在logN级别的时间花费。 由于每个点最多只会产生常数k个矩形 所以,复杂度就O(K*N*logN). 但是每个点会产生的矩形最多有8个, 所以复杂度里的K再乘上线段树的覆盖和查询次数也就相当于一个logN了, 这也是这个做法跑得比较慢的原因。 ** 至于点分治的做法,推荐这个很棒的题解: https://www.luogu.org/blog/user24559/solution-p2664** ~~(那个read()是真的被卡常了,并不是个人觉得写起来比scanf()顺手,迷...)~~ ```cpp #include<bits/stdc++.h> #define MAXN 100050 #define INF 0x3f3f3f3f using namespace std; int N,dnt,snt; int be[MAXN],en[MAXN],fa[MAXN]; long long cc[MAXN],ANS[MAXN]; struct List{ int lnt; int to[MAXN*2],nxt[MAXN*2],head[MAXN]; List():lnt(2){} void Add(int c,int u){ to[lnt]=u; nxt[lnt]=head[c]; head[c]=lnt++; } }CL,E; struct Segment{ //Every vertice can lead to (4 + the num of son*4) Plates. In the other words there are 2*4*2*N segments at most. int x,yl,yr,k; bool operator < (const Segment &rtm) const{ return x<rtm.x||(x==rtm.x&&k>rtm.k); } }S[MAXN*16]; struct SGT{ //The Segment Tree are used to maintain the cover of the line. int size,root; int ls[MAXN*2],rs[MAXN*2],cover[MAXN*2],tag[MAXN*2]; void Pushup(int u,int l,int r){ cover[u]=tag[u]?(r-l+1):cover[ls[u]]+cover[rs[u]]; } void Modify(int &u,int l,int r,int al,int ar,int k){ if(!u) u=++size; if(al<=l&&r<=ar) return (void)(tag[u]+=k,Pushup(u,l,r)); int mid=(l+r)>>1; if(al<=mid) Modify(ls[u],l,mid,al,ar,k); if(mid<ar) Modify(rs[u],mid+1,r,al,ar,k); Pushup(u,l,r); } }DT; void dfs(int u,int dad){ be[u]=++dnt; fa[u]=dad; for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(v==dad) continue; dfs(v,u); } en[u]=dnt; } void insplate(int x1,int x2,int y1,int y2){ if(x1>x2||y1>y2) return; S[++snt]=(Segment){x1,y1,y2,1}; S[++snt]=(Segment){x2+1,y1,y2,-1}; } void read(int &x){ static int sign; static char ch; x=0; sign=1; ch=getchar(); for(;ch<'0'||'9'<ch;ch=getchar()) if(ch==-'-') sign=-1; for(;'0'<=ch&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x=x*sign; } int main(){ read(N); int minc=INF,maxc=0; for(int i=1,c;i<=N;i++) read(c),CL.Add(c,i), minc=min(minc,c),maxc=max(maxc,c); for(int i=1,a,b;i<N;i++) read(a),read(b),E.Add(a,b),E.Add(b,a); dfs(1,0); for(int c=minc,u,ycover;c<=maxc;c++){ snt=0; ycover=0; for(int i=CL.head[c];i;i=CL.nxt[i]){ u=CL.to[i]; insplate(1,be[u],be[u],en[u]); insplate(be[u],en[u],1,be[u]); insplate(be[u],en[u],en[u]+1,N); insplate(en[u]+1,N,be[u],en[u]); for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(v==fa[u]) continue; insplate(be[u],be[v]-1,be[v],en[v]); insplate(be[v],en[v],be[u],be[v]-1); insplate(be[v],en[v],en[v]+1,en[u]); insplate(en[v]+1,en[u],be[v],en[v]); } } sort(S+1,S+snt+1); for(int i=1;i<=snt;i++){ cc[S[i-1].x]+=ycover; cc[S[i].x]-=ycover; DT.Modify(DT.root,1,N,S[i].yl,S[i].yr,S[i].k); ycover=DT.cover[DT.root]; } } for(int i=1;i<=N;i++) ANS[i]=ANS[i-1]+cc[i]; for(int i=1;i<=N;i++) printf("%lld\n",ANS[be[i]]); return 0; } ```