• CSP-S 模拟R2D1


    任意根求答案:换根dp(其实换根只在预处理有用。。。)

    考虑如何打暴力。

    如果有一个节点,他的子树(包含它)中有 (m) 中颜色,那么它就可以是候选答案,于是考虑如何求出所有这样的子树。

    然后有个小技巧:对于这种子树上统计的问题,可以考虑 dfs 序,因为一颗子树的 dfs 序是连续的。

    然后这里可以双指针进行操作,然后每次贪心地找最小的一段,再判断是否为子树(若不是则一定可以不为答案,(l) 在之前 ((l,r) 的 LCA)时就已考虑)。

    考虑每个丶i,则 (T) 可分类为在 i 的子树上的和不在的。在的可以按上述方法求,即为符合条件的 i 向上的最大值。

    然后我们可以预处理出每个丶向上和向下的最长连,然后考虑 (T) 不在的情况。

    首先 i 的子树的 dfs 序是连续的,而实际上 dfs 序可以构成一个环,因此不在子树点上的 dfs 序也是连续的,于是我们可以断环成链,然后处理,注释中有解释。

    主要启发:1.不定根-换根 dp,还可以枚举无根树的所有子树,用定根+枚举子树与补树的方法来遍历原树的所有子树

    2.处理子树上的问题-dfs 序,要处理补树可以考虑环形的 dfs 序

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+7;
    const int M=1e5+7;
    template <class I>
    inline void read(I &x){
        int f=1;
        char c;
        for(c=getchar();c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
        for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+(c&15),c=getchar());
        x*=f;
    }
    int tot=1,head[N],ver[2*N],nxt[2*N],a[N],f[N],g[N],f2[N],T,rnk[2*N],ans,sz[N],n,m,c[N];
    inline void add(int x,int y){
    	ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
    }
    void dfs(int x,int fa){
    	rnk[++T]=x;
    	sz[x]=1;
    	f[x]=1;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y==fa) continue;
    		dfs(y,x);
    		sz[x]+=sz[y];
    		if(f[y]+1>f[x])
    			f2[x]=f[x],f[x]=f[y]+1;
    		else if(f[y]+1>f2[x])
    			f2[x]=f[y]+1;
    	}
    }
    void dfs2(int x,int fa){
    	g[x]=g[fa]+1;
    	if(f[x]+1<f[fa]) g[x]=max(g[x],f[fa]+1);
    	else g[x]=max(g[x],f2[fa]+1);
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y==fa) continue;
    		dfs2(y,x);
    	}
    }
    inline void calc1(){
    	int l,r,cnt=0;
    	l=r=n;
    	while(l>=1){//注意 l 实际上是枚举的 i,然后用贪心的思想判断子树是否合法,若固定 r 则求出来的东西没任何意义
    		if(!c[a[rnk[l]]]++) ++cnt;
    		while(cnt==m&&c[a[rnk[r]]]>1) --c[a[rnk[r--]]];
    		if(cnt==m&&r<l+sz[rnk[l]])
    			ans=max(ans,g[rnk[l]]-1);
    		--l;
    	}
    }
    inline void calc2(){
    	int l,r,cnt=0;
    	l=r=1;
    	while(r<=2*n){//这里 r 实际上并不是枚举的 i,因为我们把 r 的颜色统计进去了,所以 r 实际上是 i-1
    		if(!c[a[rnk[r]]]++) ++cnt;
    		while(cnt==m&&c[a[rnk[l]]]>1) --c[a[rnk[l++]]];
    		if(cnt==m&&r>n&&l>=r-n+1+sz[rnk[r+1]])//r>n 显然要满足,然后结合上述意义可知 l 一定不在 r+1 的子树中
    			ans=max(ans,f[rnk[r+1]]);
    		++r;
    	}
    }
    int main(){
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	read(n),read(m);
    	for(int i=1;i<=n;i++)
    		read(a[i]);
    	for(int i=1;i<n;i++){
    		int x,y;
    		read(x),read(y);
    		add(x,y),add(y,x);
    	}
    	g[0]=-1;
    	dfs(1,0);
    	dfs2(1,0);
    	for(int i=1;i<=n;i++)
    		rnk[i+n]=rnk[i];
    	calc1();
     	memset(c,0,sizeof(c));
    	calc2();
    	printf("%d
    ",ans+1);
    	return 0;
    }
    
  • 相关阅读:
    LeNet && ModernCNN
    Fundamentals of Convolutional Neural Networks
    机器及其相关技术介绍
    学而后思,方能发展;思而立行,终将卓越
    贪心的区间问题
    基环树
    模板类
    存储问题
    大佬们的技巧
    exgcd
  • 原文地址:https://www.cnblogs.com/Hikigaya/p/11729993.html
Copyright © 2020-2023  润新知