• [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
    ",ans[x]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    2019 安装SSDT
    添加删除服务
    POWER BI 报表服务器搭建配置
    解决POWER BI权限控制问题
    asp.net core 发布后显示异常错误信息的方法
    Microsoft SQL Server 查询连接数和关闭连接数
    不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况
    SQL Server在所有表中查找内容(在整个库搜索内容)
    IIS 在.NET MVC网站下创建多个子应用程序,子站访问根目录问题
    博客网址
  • 原文地址:https://www.cnblogs.com/Chtholly/p/11762772.html
Copyright © 2020-2023  润新知