zoukankan      html  css  js  c++  java
  • loj 3014「JOI 2019 Final」独特的城市

    loj

    我本来是直接口胡了一个意思一样的做法的,但是因为觉得有点假+实现要用并查集(?)就卡了好一会儿...

    对于一个点(x)来说,独特的点一定在它的最长链上,如果有独特的点不在最长链上,那么最长链上一定有和他到(x)距离相同的点,矛盾

    然后对于一个点,最长链端点一定可以是直径的两端点之一,所以如果我们分别以树的直径的两端点为根进行dfs,那么一个点在其中一次dfs中,独特的点都会在到根的路径上,所以我们用栈维护到根的点,然后不同颜色数开桶来维护,每次压栈或弹栈时改变桶内元素个数,然后根据某个桶元素变化维护当前答案

    然后在点(x)到根的路径上,其他挂出去的多余的链会往上覆盖掉一些点(也就是覆盖掉到(x)距离为某个值的点,它们不是独特的),导致这些点不能贡献答案.所以在dfs进别的子树时要用这些没用到的链来删掉一些点.具体来讲,dfs某个点时把父亲压入栈;在之前先预处理一个点子树内最长链长度以及次长链长度(最长链次长链要来自不同子树),然后先把到(x)距离(le)次长链长度的栈里的点弹掉,递归处理最长链所在子树,再把到(x)距离(le)最长链长度的栈里的点弹掉,这个时候维护的颜色种数就是当前这个点的答案,可以直接更新答案,之后递归其他子树处理.每个点答案为两个直径端点算出的值的max

    #include<bits/stdc++.h>
    #define LL long long
    #define uLL unsigned long long
    #define db double
    
    using namespace std;
    const int N=2e5+10;
    int rd()
    {
    	int x=0,w=1;char ch=0;
    	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    	return x*w;
    }
    int to[N<<1],nt[N<<1],hd[N],tot=1;
    void add(int x,int y)
    {
    	++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
    	++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
    }
    int n,m,an[N],a[N],mx,r1,r2;
    void dd(int x,int ffa,int de)
    {
    	if(mx<de) mx=de,r2=x;
    	for(int i=hd[x];i;i=nt[i])
    	{
    		int y=to[i];
    		if(y==ffa) continue;
    		dd(y,x,de+1);
    	}
    }
    int de[N],dp[N],dp2[N],hs[N],stk[N],tp,bk[N],na;
    void dfs1(int x,int ffa)
    {
    	hs[x]=dp[x]=dp2[x]=0;
    	if(ffa&&!nt[hd[x]]){dp[x]=de[x];return;}
    	for(int i=hd[x];i;i=nt[i])
    	{
    		int y=to[i];
    		if(y==ffa) continue;
    		de[y]=de[x]+1,dfs1(y,x);
    		if(dp[y]>=dp[x]) hs[x]=y,dp2[x]=dp[x],dp[x]=dp[y];
    		else if(dp[y]>=dp2[x]) dp2[x]=dp[y];
    	}
    }
    void dfs3(int x,int ffa)
    {
    	if(ffa) stk[++tp]=ffa,na+=!bk[a[ffa]],++bk[a[ffa]];
    	if(!hs[x]) an[x]=max(an[x],na);
    	else
    	{
    		while(tp&&de[x]-de[stk[tp]]<=dp2[x]-de[x])
    			--bk[a[stk[tp]]],na-=!bk[a[stk[tp]]],--tp;
    		dfs3(hs[x],x);
    		while(tp&&de[x]-de[stk[tp]]<=dp[x]-de[x])
    			--bk[a[stk[tp]]],na-=!bk[a[stk[tp]]],--tp;
    		an[x]=max(an[x],na);
    		for(int i=hd[x];i;i=nt[i])
    		{
    			int y=to[i];
    			if(y==ffa||y==hs[x]) continue;
    			dfs3(y,x);
    		}
    	}
    	if(ffa&&stk[tp]==ffa) --bk[a[ffa]],na-=!bk[a[ffa]],--tp;
    }
    void wk(int rt)
    {
    	de[rt]=0,dfs1(rt,0);
    	dfs3(rt,0);
    }
    
    int main()
    {
    	n=rd(),m=rd();
    	for(int i=1;i<n;++i) add(rd(),rd());
    	for(int i=1;i<=n;++i) a[i]=rd();
    	mx=-1,dd(1,0,0),r1=r2;
    	mx=-1,dd(r1,0,0);
    	wk(r1),wk(r2);
    	for(int i=1;i<=n;++i) printf("%d
    ",an[i]);
    	return 0;
    }
    
  • 相关阅读:
    《CLR Via C# 第3版》笔记之(十四) 泛型高级
    《CLR Via C# 第3版》笔记之(十三) 泛型基础
    AOP学习基于Emit和Attribute的简单AOP实现
    《CLR Via C# 第3版》笔记之(十五) 接口
    C# 连接Oracle(利用ODP.net,不安装oracle客户端)
    《CLR Via C# 第3版》笔记之(十七) 线程基础
    C#直接读取磁盘文件(类似linux的Direct IO模式)
    《CLR Via C# 第3版》笔记之(十六) 字符串
    [置顶] C#中通过调用webService获取上网IP地址的区域的方法
    Android中Socket通讯类
  • 原文地址:https://www.cnblogs.com/smyjr/p/11657526.html
Copyright © 2011-2022 走看看