树上启发式合并

补题时碰见的骚操作。可以在nlogn的时间内处理一些没有修改的,对子树的查询问题。

具体方法为

1.轻重链剖分,找出重儿子。(预处理)

2.先处理轻儿子,不记录 贡献。

3.处理重儿子,记录贡献。

4.将当前节点和轻儿子的贡献和重儿子合并。

5.如果当前节点是轻儿子的话,消除影响。

算法的正确性很容易理解。重儿子不用清空了, 因为我的兄弟节点都已经处理完了,我不会影响到兄弟节点。

时间复杂度不会证明。

hdu 4358

题目大意:询问每个节点颜色出现次数为k的颜色有多少个。

解题思路:由于对子树只有查询没有修改,可以dfs序+莫队。当时。。树上启发式合并更无脑,更快。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define pb(x) push_back(x)
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define N 100005

vector<int>G[N];
int sz[N],son[N];
int cnt[N],a[N],ou[N];
int b[N];
int Son,sum;
int n,k;

void dfs1(int u,int fa)
{
    sz[u]=1;
    int mx=-1;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v!=fa)
        {
            dfs1(v,u);
            sz[u]+=sz[v];
            if(sz[v]>mx)mx=sz[v],son[u]=v;
        }
    }
}

void add(int u,int fa,int val)
{
    if(cnt[a[u]]==k)sum--;
    cnt[a[u]]+=val;
    if(cnt[a[u]]==k)sum++;
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==fa||v==Son)continue;
        add(v,u,val);
    }
}

void dfs(int u,int fa,int f)
{
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v==fa||v==son[u])continue;
        dfs(v,u,0);
    }
    if(son[u])dfs(son[u],u,1),Son=son[u];
    add(u,fa,1);
    ou[u]=sum;
    if(son[u])Son=0;
    if(!f)add(u,fa,-1),sum=0;
}

int main()
{
    int t;
    sca(t);
    for(int cas=1; cas<=t; cas++)
    {
        sca(n),sca(k);
        memset(son,0,sizeof(son));
        sum=0,Son=0;
        rep(i,1,n)G[i].clear();
        rep(i,1,n)sca(a[i]),b[i]=a[i];
        sort(b+1,b+1+n);
        int len=unique(b+1,b+1+n)-b-1;
        rep(i,1,n)a[i]=lower_bound(b+1,b+1+len,a[i])-b;
        rep(i,1,n-1)
        {
            int x,y;
            sca(x),sca(y);
            G[x].pb(y);
            G[y].pb(x);
        }
        dfs1(1,-1);
        dfs(1,-1,0);
        int q;
        sca(q);
        printf("Case #%d:\n",cas);
        while(q--)
        {
            int tmp;
            sca(tmp);
            printf("%d\n",ou[tmp]);
        }
        if(cas!=t)printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/88735415