题解 P5588 【小猪佩奇爬树】

小菜鸟

2019-10-13 14:42:35

Solution

简述题意:树上每个点有一种颜色。对于每种颜色,求经过**所有该颜色的点**的路径数量。 --- 刚看完这题秒出一个$O(n^2)$算法,准备拿60...因为是暴力分就只开了$n=10000$的空间 然后考完发现这个玩意跑得挺快的 然后开大空间,开`long long`交一发( **过了。。。** 我\*\*\*\*\* --- 首先我们可以想到,对于一种颜色的点,如果存在这样的路径,则只有两种情况: * 存在一个深度最大的点,其他这种颜色的点都是其祖先,形成一条链。 * 存在两条如上一条所述且互不相交的链 如果都不是,直接判无解就行。 而对于不存在的颜色,答案显然是$\frac{n(n-1)}{2}$ 然后上面这两种情况都会得到一条经过所有该颜色的点的最短路径。 把链上面的树枝全部砍掉,只剩下两个端点$u,v​$上的子树 我们发现答案就是两个子树大小乘起来。 交一发,WA 0 pts... 我们可以发现这条链可能是一个点。那么把以该点为根时所有子树的大小两两相乘就是答案。。。 然后就可以$O(n^2)$\*过去了。。。 我找链用了set+暴力跳父亲,因此复杂度是错的...不知道正解该怎么做... ```cpp #include<cstdio> #include<set> #define int long long const int N=1<<15; int n,head[N],size[N],fa[N],dep[N],col[N]; struct Edge { int next,to; }; Edge E[N<<1]; void add(int u,int v) { static int tot=0; E[++tot].next=head[u]; E[tot].to=v; head[u]=tot; } std::set<std::pair<int,int> > pts[N]; void dfs(int u) { size[u]=1; pts[col[u]].insert(std::make_pair(-dep[u],u)); for(int i=head[u];i;i=E[i].next) { int v=E[i].to; if(v==fa[u])continue; fa[v]=u; dep[v]=u+1; dfs(v); size[u]+=size[v]; } } int check_ans(int c) { if(pts[c].empty())return n*(n-1)/2; if(pts[c].size()==1) { int u=(*pts[c].begin()).second; int sum=0,res=0; for(int i=head[u];i;i=E[i].next) { int v=E[i].to; if(v==fa[u])continue; res+=sum*size[v]; sum+=size[v]; } if(fa[u]) { res+=(n-size[u])*sum; } return res+n-1; } int u=(*pts[c].begin()).second,v,t=u; pts[c].erase(std::make_pair(-dep[u],u)); int except=u; while(fa[t]) { if(col[fa[t]]==c) { v=fa[t]; except=t; pts[c].erase(std::make_pair(-dep[fa[t]],fa[t])); } t=fa[t]; } if(pts[c].empty()) { return size[u]*(n-size[except]); } v=(*pts[c].begin()).second,t=v; pts[c].erase(std::make_pair(-dep[v],v)); while(fa[t]) { if(col[fa[t]]==c) { if(pts[c].erase(std::make_pair(-dep[fa[t]],fa[t]))==0)return 0; } t=fa[t]; } if(!pts[c].empty())return 0; return size[u]*size[v]; } signed main() { scanf("%lld",&n); for(int i=1;i<=n;++i)scanf("%lld",col+i); for(int i=1;i<n;++i) { int u,v; scanf("%lld%lld",&u,&v); add(u,v); add(v,u); } dfs(1); for(int i=1;i<=n;++i)printf("%lld\n",check_ans(i)); } ```