zoukankan      html  css  js  c++  java
  • AtCoder abc163_f

    爬爬爬

    这其实算是ABC的F们中比较难的一个

    洛谷题目页面传送门 & AtCoder题目页面传送门

    有一棵树(T=(V,E),|V|=n,|E|=n-1),节点(i)的颜色为(a_iin[1,n])。求(forall iin[1,n]),经过至少(1)个颜色为(i)的节点的路径个数。

    (ninleft[1,2 imes10^5 ight])

    先考虑对于某个(i),该如何求答案。注意到,若正面求答案,那么需要容斥一大堆,或者点分治(我还不会),比较麻烦。考虑反面求答案,求不经过任何颜色为(i)的节点的路径个数,最后用总路径数(dfrac{n(n+1)}2)减去它即可。考虑“不经过任何颜色为(i)的节点的路径个数”怎么算,显然,若将所有颜色为(i)的节点删除,会分离成若干CC,设它们的大小分别为(s_1,s_2,cdots,s_m),那么答案就是(sumlimits_{j=1}^mdfrac{s_j(s_j+1)}2),因为一条路径不经过任何颜色为(i)的节点当且仅当它的两端点属于同一个CC。

    如果只对于一个(i)求答案,那么显然可以直接删除所有该删的点,然后随便DFS一下就(mathrm O(n))出答案了。但现在需要在较短时间内求出对于所有(i)的答案,就有那么一丝丝难度了。不难发现,(forall i),任意一个CC的深度最小的点要么没有父亲(等于根,令(1)为根),要么父亲颜色为(i)(反证法,如果不成立,那么显然CC可以再往上扩展)。于是我们可以用这个唯一的(唯一性可以反证,如果不唯一,也就是(2)个同辈人没有长辈连接,一定不连通)深度最小的点作为整个CC的代表。

    又不难发现,任意一个非(1)节点,都只能作为一个(i)剖出来的CC的代表,因为它们都有一个固定的父亲,也就有一个固定的父亲的颜色,而节点(1)却可以作为任意一个(i)剖出来的CC的代表,需要特殊开数组处理。所以总CC数为(mathrm O(n))。考虑一遍DFS求出所有CC的大小。考虑如何算一个CC的大小。先假设整个以(x)为根的子树全属于此CC,然后看有多少是被删了的或属于其他CC的,减去即可。显然,在子树内的所有CC代表要么一个为祖先一个为晚辈,此时以它们为根的子树一个包含另一个,要么无关,此时以它们为根的子树不相交,CC代表们的父亲(颜色为(i))亦然。又显然,所有子树内的以颜色为(i)的节点为根的子树内的节点都不属于此CC,于是我们只需要将此CC的大小减去所有不为其他颜色为(i)的节点的晚辈的颜色为(i)的节点的子树大小即可。考虑从下往上贡献,即DFS过程中时刻维护递归栈,每到一个节点就将最深的一个颜色相同的节点(这个可以时刻维护每种颜色的递归栈(mathrm O(1))做到)在栈中的下一个节点的CC大小减去它的子树大小(正确性显然)。对于根代表多CC的情况,特判一下即可。

    时间复杂度(mathrm O(n))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define ppb pop_back
    typedef long long ll;
    const int N=200000;
    int n;//节点个数 
    int a[N+1];//颜色 
    vector<int> nei[N+1];//邻接表 
    int sz[N+1]/*子树大小*/,cc_sz[N+1]/*CC大小(非根)*/,cc_sz1[N+1]/*CC大小(根,下标为颜色)*/;
    int fa[N+1];//父亲 
    vector<int> stk/*总递归栈*/,stk_c[N+1]/*颜色们的递归栈*/;
    void dfs(int x=1){//求CC们的大小 
    //	cout<<x<<"
    ";
    	sz[x]=1;
    	stk_c[a[x]].pb(stk.size());
    	stk.pb(x);//压栈 
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa[x])continue;
    		fa[y]=x;
    		dfs(y);
    		sz[x]+=sz[y];
    	}
    	stk.ppb();
    	stk_c[a[x]].ppb();//准备回溯,弹出 
    	if(stk_c[a[x]].size()>1)cc_sz[stk[stk_c[a[x]].back()+1]]-=sz[x];//贡献(非根) 
    	else/*特判*/ cc_sz1[a[x]]-=sz[x];//贡献(根) 
    }
    ll ans[N+1];
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	for(int i=1;i<n;i++){
    		int x,y;
    		cin>>x>>y;
    		nei[x].pb(y);nei[y].pb(x);
    	}
    	stk.pb(0);
    	for(int i=1;i<=n;i++)stk_c[i].pb(0);
    	dfs();
    	for(int i=1;i<=n;i++)cc_sz[i]+=sz[i],cc_sz1[i]+=n;
    //	for(int i=1;i<=n;i++)cout<<cc_sz[i]<<" ";puts("");
    //	for(int i=1;i<=n;i++)cout<<cc_sz1[i]<<" ";puts("");
    	for(int i=1;i<=n;i++)ans[i]=1ll*n*(n+1)/2;
    	for(int i=2;i<=n;i++)ans[a[fa[i]]]-=1ll*cc_sz[i]*(cc_sz[i]+1)/2;
    	for(int i=1;i<=n;i++)ans[i]-=1ll*cc_sz1[i]*(cc_sz1[i]+1)/2;//减去反面答案 
    	for(int i=1;i<=n;i++)cout<<ans[i]<<"
    ";
    	return 0;
    }
    
  • 相关阅读:
    第二章、Java内存区域与内存溢出异常
    第二章、Java内存区域与内存溢出异常
    腾讯//格雷编码
    腾讯//格雷编码
    数据结构5.5_广义表的递归算法
    数据结构5.4_m元多项式的表示
    数据结构4.2_串操作应用举例_建立词索引表
    数据结构3_栈和队列
    数据结构2_线性表
    数据结构1_绪论
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc163-f.html
Copyright © 2011-2022 走看看