[CSP校内集训]ac(树上启发式合并)

题意

有一棵树,每个节点\(i\)有一定的容量\(k_i\)(只能装\(k_i\)个颜色);有\(m\)次操作,每次给\(x\)\(1\)路径上的所有点加上一个颜色\(c\);修改操作完成后询问每个节点有多少种不同的颜色\((n,m,k_i \leq 10^5)\)

思路

30pts数据小可以直接暴力跳

另外40pts有\(k_i=10^5\)可以类比雨天的尾巴


可以看出来对一个点的答案有影响的操作都在它的子树中

这种问题,显然要么维护颜色(值域线段树),要么维护时间(修改操作的顺序),而值域线段树考虑不到\(k\)的约束,只有40pts,所以考虑维护时间

于是按修改操作的顺序作为下标维护线段树,一个点维护两个值:\(siz\):该子树有多少个操作;\(sum\):该子树中实际有贡献的操作数

什么叫实际有贡献的操作?就是说同一个子树中,如果两个同样颜色的操作先后发生,那后发生的操作就没有贡献,也就是\(siz+1\)\(sum\)不变

有了这两个变量,在线段树上按照\(k_i\)二分即可求出\(i\)节点的答案

但不可能每个点都建一棵线段树,由于一个点只考虑其子树中的操作,线段树可以自底向上合并,于是就成了树上启发式合并,保证所有操作的复杂度不基于重儿子就可以保证复杂度没问题

这道题思路类似,就是把\(trie\)换成了时间上的线段树

时间复杂度\(O(nlog^2n)\)

Code

#include<bits/stdc++.h>
#define N 100005
#define Min(x,y) ((x)<(y)?(x):(y))
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
typedef long long ll;
int n,m,q,k[N],ans[N];
int sum[N<<2],size[N<<2],sig[N<<2];
int son[N],fa[N],t[N<<2];
vector< pair<int,int> > co[N];
map<int,int> mp;//颜色还有负数,cao 
int colsum=0;

struct Edge
{
    int next,to;
}edge[N<<1];int head[N],cnt=1;

void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}
template <class T>
void read(T &x)
{
    char c; int sign=1;
    while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
    while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
void dfs1(int rt)
{
    size[rt]=1;
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt]) continue;
        fa[v]=rt;
        dfs1(v);
        size[rt]+=size[v];
        if(size[son[rt]]<size[v]) son[rt]=v;
    }
}

void pd(int rt)
{
    if(!sig[rt]) return;
    sig[rt<<1]=sig[rt<<1|1]=1;
    size[rt<<1]=size[rt<<1|1]=sum[rt<<1]=sum[rt<<1|1]=0;
    sig[rt]=0;
}
void modify(int rt,int l,int r,int x,int val,int siz)//颜色数量,时间数量 
{
    if(l==r) { sum[rt]+=val; size[rt]+=siz; return; }
    int mid=(l+r)>>1;
    pd(rt);
    if(x<=mid) modify(rt<<1,l,mid,x,val,siz);
    else modify(rt<<1|1,mid+1,r,x,val,siz);
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    size[rt]=size[rt<<1]+size[rt<<1|1];
}
int query(int rt,int l,int r,int k)
{
    if(k<=0) return 0;
    if(l==r) return sum[rt];
    int mid=(l+r)>>1;
    pd(rt);
    if(size[rt<<1]<=k) return sum[rt<<1] + query(rt<<1|1,mid+1,r,k-size[rt<<1]);
    return query(rt<<1,l,mid,k);
}
void add(int rt)
{
    for(int i=0,c=co[rt].size();i<c;++i)
    {
        int col=co[rt][i].first,tim=co[rt][i].second;
        if(!t[col])//第一次加入该颜色 
        {
            t[col]=tim;
            modify(1,1,m,tim,1,0);
        }
        else if(t[col]>tim)
        {
            modify(1,1,m,t[col],-1,0);
            modify(1,1,m,tim,1,0);
            t[col]=tim;
        }
        modify(1,1,m,tim,0,1);
    }
}
void clr(int rt)
{
    size[1]=sum[1]=sig[1]=1;
    for(int i=0,c=co[rt].size();i<c;++i) t[co[rt][i].first]=0;
}
void cv(int x,int y)
{
    for(int i=0,c=co[y].size();i<c;++i) co[x].push_back(co[y][i]);
    co[y].clear();
}
void dfs(int rt)
{
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt] || v==son[rt]) continue;
        dfs(v); clr(v);
    }
    if(son[rt]) dfs(son[rt]);
    add(rt);
    for(int i=head[rt];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa[rt] || v==son[rt]) continue;
        add(v);
    }
    ans[rt]=query(1,1,m,k[rt]);
    if(son[rt])
    {
        cv(son[rt],rt);
        swap(co[rt],co[son[rt]]);
//      cv(rt,son[rt]);直接这样复杂度是错的qwq 
        for(int i=head[rt];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=fa[rt]) cv(rt,v);
        }
    }
}

int main()
{
    freopen("ac.in","r",stdin);
    freopen("ac.out","w",stdout);
    read(n);
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x);read(y);
        add_edge(x,y);
        add_edge(y,x);
    }
    dfs1(1);
    for(int i=1;i<=n;++i) read(k[i]);
    read(m);
    for(int i=1;i<=m;++i)
    {
        int x,c;
        read(x);read(c);
        if(!mp[c]) mp[c]=++colsum;
        c=mp[c];
        co[x].push_back(make_pair(c,i));
    }
    memset(size,0,sizeof(size));
    dfs(1);
    read(q);
    while(q--)
    {
        int x; read(x);
        printf("%d\n",ans[x]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Chtholly/p/11762772.html