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\n",ans+1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Hikigaya/p/11729993.html
今日推荐