zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试]:模板(ac)(线段树启发式合并)

    题目描述

    辣鸡$ljh NOI$之后就退役了,然后就滚去学文化课了。
    他每天都被$katarina$大神虐,仗着自己学过一些姿势就给$katarina$大神出了一道题。
    有一棵$n$个节点的以$1$号节点为根的树,每个节点上有一个小桶,节点$u$上的小桶可以容纳个小球,$ljh$每次可以给一个节点到根路径上的所有节点的小桶内放一个小球,如果这个节点的小桶满了则不能放进这个节点,在放完所有小球之后就企图去刁难$katarina$大神,让$katarina$大神回答每个节点的小桶内的小球有多少种颜色。
    然而$katarina$大神一眼就秒掉了,还说这就是一道傻逼模板题。
    现在$katarina$大神想考考即将参加$NOIP2019$的你能不能回答上辣鸡$ljh$的问题。


    输入格式

    第一行,一个整数$n$,树上节点的数量。
    接下来$n-1$行,每行两个整数$u,v$,表示在$u,v$之间有一条边。
    接下来一行$n$个整数,$k_1~k_n$表示每个节点上的小桶数量。
    下一行是一个整数$m$,表示$ljh$进行的操作数量。
    接下来$m$行,每行两个整数$x,c$,分别表示进行操作的节点和小球颜色。
    下一行是一个整数$Q$,表示你需要回答的询问数。
    接下来$Q$行,每行一个整数$x$,表示一个询问。


    输出格式

    对于每个询问输出一行表示这个询问的答案。


    样例

    样例输入1:

    5
    1 2
    2 3
    3 4
    2 5
    2 1 1 1 1
    2
    2 1
    4 2
    3
    1
    3
    5

    样例输出1:

    2
    1
    0

    样例输入2:

    10
    3 10
    2 5
    3 2
    2 6
    1 9
    8 7
    7 4
    3 8
    3 1
    15 47 23 22 9 16 45 39 21 13
    10
    10 7
    9 3
    5 1
    5 2
    9 4
    10 9
    2 4
    10 1
    2 6
    7 9
    3
    1
    2
    3

    样例输出2:

    7
    4
    6


    数据范围与提示

    对于$5\%$的数据,$nleqslant 10,mleqslant 10,k_ileqslant 10$。

    对于$30\%$的数据,$nleqslant {10}^3,mleqslant {10}^3,k_ileqslant {10}^3$。

    对于另外$40\%$的数据,$nleqslant {10}^5,mleqslant {10}^5,k_i={10}^5$。

    对于$100\%$的数据,$nleqslant {10}^5,mleqslant {10}^5,k_ileqslant {10}^5$


    题解

    $5\%$算法:

    到现在我都没想出来。

    $30\%$算法:

    暴力修改,$Theta(1)$查找答案。

    时间复杂度:$Theta(n^2)$。

    期望得分:$30$分。

    $40\%$算法:

    注意$k_i={10}^5$,也就是说,每一个桶肯定不会装超,问题也就简单了许多,转化成了[BZOJ3307]:雨天的尾巴这道题。

    区别在于,这道题是维护总和,而那道题是维护最大值,而且这道题还不用LCA。

    时间复杂度:$Theta(nlog n)$。

    期望得分:$40$分。

    总得分:$70$分。

    $100\%$算法:

    对于每一个结点都用一棵线段树维护,下标为时间,存储这个时间子树中是否已经加入了小球和贡献(注意如果之前已经加入了这个小球则贡献为0)。

    在线段树上二分查找答案就好了。

    但是,显然这样还不够,无论是时间还是空间。

    所以我们使用线段树启发式合并,利用启发式合并的方式处理出当前子树中的所有操作,同时构建出新的线段树。

    我们还可以先预处理出重儿子,这样我们就可以更少的改变当前线段树里的值。

    时间复杂度:$Theta(nlog^2n)$。

    期望得分:$100$分。


    代码时刻

    $30\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[200001];
    int N,M,Q;
    int head[100001],cnt;
    int k[100001];
    int fa[100001];
    bool Map[1001][1001];
    int ans[100001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void pre_dfs(int x)//预处理
    {
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa[x])
    		{
    			fa[e[i].to]=x;
    			pre_dfs(e[i].to);
    		}
    }
    void ufs(int x,int c)//疯狂往上找,暴力修改
    {
    	while(1)
    	{
    		if(!x)break;
    		if(!Map[x][c]&&k[x]>0)ans[x]++;
    		Map[x][c]=1;
    		k[x]--;
    		x=fa[x];
    	}
    }
    int main()
    {
    	scanf("%d",&N);
    	for(int i=1;i<N;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v);
    		add(v,u);
    	}
    	for(int i=1;i<=N;i++)
    		scanf("%d",&k[i]);
    	scanf("%d",&M);
    	if(N<=1000&&M<=1000)
    	{
    		pre_dfs(1);
    		while(M--)
    		{
    			int x,c;
    			scanf("%d%d",&x,&c);
    			ufs(x,c);
    		}
    		scanf("%d",&Q);
    		while(Q--)
    		{
    			int x;
    			scanf("%d",&x);
    			printf("%d
    ",ans[x]);
    		}
    	}
    	return 0;
    }
    

    $40\%$算法:

    #include<bits/stdc++.h>
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[200001];
    struct node
    {
    	int x;
    	int c;
    }q[100001];
    int N,M,Q;
    int head[100001],cnt;
    int root[100001],trsum[10000001],ls[10000001],rs[10000001],tot;
    int k[100001];
    int dfsv[100001];
    bool vis[100001];
    int sum;
    int ans[100001];
    bool cmp(node a,node b){return a.c<b.c;}
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void insert(int &x,int k,int l,int r)
    {
        if(!x)x=++tot;
        if(l==r)
        {
        	trsum[x]=1;
            return;
        }
        int mid=(l+r)>>1;
        if(k<=mid)insert(ls[x],k,l,mid);
        else insert(rs[x],k,mid+1,r);
        trsum[x]=trsum[ls[x]]+trsum[rs[x]];
    }
    int merge(int x,int fl,int l,int r)//合并
    {
    	if(!x||!fl)return x+fl;
    	if(l==r)
    	{
    		trsum[x]=trsum[x]||trsum[fl];
    		return x;
    	}
        int mid=(l+r)>>1;
        ls[x]=merge(ls[x],ls[fl],l,mid);
    	rs[x]=merge(rs[x],rs[fl],mid+1,r);
        trsum[x]=trsum[ls[x]]+trsum[rs[x]];
        return x;
    }
    void dfs(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!vis[e[i].to])
    		{
    			dfs(e[i].to);
    			root[x]=merge(root[x],root[e[i].to],1,M);
    		}
    	ans[x]=trsum[root[x]];
    }
    int main()
    {
    	scanf("%d",&N);
    	for(int i=1;i<N;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v);
    		add(v,u);
    	}
    	for(int i=1;i<=N;i++)
    		scanf("%d",&k[i]);
    	scanf("%d",&M);
    	for(int i=1;i<=M;i++)
    		scanf("%d%d",&q[i].x,&q[i].c);
    	sort(q+1,q+M+1,cmp);
    	for(int i=1;i<=M;i++)
    	{
    		if(q[i].c!=q[i-1].c)sum++;//算是一个预处理吧,思路有些清奇
    		insert(root[q[i].x],sum,1,M);
    	}
    	dfs(1);
    	scanf("%d",&Q);
    	while(Q--)
    	{
    		int x;
    		scanf("%d",&x);
    		printf("%d
    ",ans[x]);
    	}
    	return 0;
    }
    

    $100\%$算法:

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    struct rec
    {
    	int nxt;
    	int to;
    }e[200001];
    struct node
    {
    	int x;
    	int c;
    }q[100001];
    int N,M,Q;
    int head[100001],cnt;
    int t[100001];
    map<int,int> col;//会有负的权值,所以用map
    int sum;
    bool vis[100001];
    int son[100001];
    int tr[400001],lz[400001],tle[400001],size[400001];
    int ans[100001];
    vector<pair<int,int> >super[100001];//用vector存储每一个点内球的颜色和时间,二维数组会MLE
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void super_memset(int x)//清空
    {
    	tr[1]=size[1]=0;
    	lz[1]=1;
    	for(int i=0;i<super[x].size();i++)tle[super[x][i].first]=0;
    }
    void pushup(int x)
    {
    	tr[x]=tr[L(x)]+tr[R(x)];
    	size[x]=size[L(x)]+size[R(x)];
    }
    void pushdown(int x)
    {
    	if(!lz[x])return;
    	size[L(x)]=size[R(x)]=tr[L(x)]=tr[R(x)]=lz[x]=0;
    	lz[L(x)]=lz[R(x)]=1;
    }
    void change(int x,int l,int r,int k,int v,int w)
    {
    	tr[x]+=v;
    	size[x]+=w;
    	if(l==r)return;
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(k<=mid)change(L(x),l,mid,k,v,w);
    	else change(R(x),mid+1,r,k,v,w);
    	pushup(x);
    }
    void add(int x)
    {
    	for(int i=0;i<super[x].size();i++)
    	{
    		pair<int,int> flag=super[x][i];
    		if(!tle[flag.first])
    		{
    			change(1,1,M,flag.second,1,0);
    			tle[flag.first]=flag.second;
    		}
    		else if(tle[flag.first]>flag.second)
    		{
    			change(1,1,M,tle[flag.first],-1,0);
    			change(1,1,M,flag.second,1,0);
    			tle[flag.first]=flag.second;
    		}
    		change(1,1,M,flag.second,0,1);
    	}
    }
    int ask(int x,int l,int r,int k)
    {
    	if(k<=0)return 0;
    	if(l==r)return tr[x];
    	pushdown(x);
    	int mid=(l+r)>>1;
    	if(size[L(x)]<=k)return tr[L(x)]+ask(R(x),mid+1,r,k-size[L(x)]);
    	else return ask(L(x),l,mid,k);
    }
    void connect(int x,int y)
    {
    	for(int i=0;i<super[y].size();i++)
    		super[x].push_back(super[y][i]);
    	super[y].clear();
    }
    void pre_dfs(int x)
    {
    	size[x]=super[x].size();
    	vis[x]=1;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!vis[e[i].to])
    		{
    			pre_dfs(e[i].to);
    			size[x]+=size[e[i].to];
    			if(size[e[i].to]>size[son[x]])son[x]=e[i].to;
    		}
    }
    void pro_dfs(int x,int fa)
    {
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa&&e[i].to!=son[x])
    		{
    			pro_dfs(e[i].to,x);
    			super_memset(e[i].to);
    		}
    	if(son[x])pro_dfs(son[x],x);
    	add(x);
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fa&&e[i].to!=son[x])
    			add(e[i].to);
    	ans[x]=ask(1,1,M,t[x]);
    	if(son[x])
    	{
    		connect(son[x],x);
    		swap(super[x],super[son[x]]);
    		for(int i=head[x];i;i=e[i].nxt)
    			if(e[i].to!=fa)
    				connect(x,e[i].to);
    	}
    }
    int main()
    {
    	scanf("%d",&N);
    	for(int i=1;i<N;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v);
    		add(v,u);
    	}
    	for(int i=1;i<=N;i++)
    		scanf("%d",&t[i]);
    	scanf("%d",&M);
    	for(int i=1;i<=M;i++)
    	{
    		scanf("%d%d",&q[i].x,&q[i].c);
    		if(!col[q[i].c])
    		{
    			col[q[i].c]=++sum;
    			q[i].c=sum;
    		}
    		else q[i].c=col[q[i].c];
    		super[q[i].x].push_back(make_pair(q[i].c,i));
    	}
    	pre_dfs(1);
    	memset(size,0,sizeof(size));
    	pro_dfs(1,0);
    	scanf("%d",&Q);
    	while(Q--)
    	{
    		int x;
    		scanf("%d",&x);
    		printf("%d
    ",ans[x]);
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    encodeURI() 的用法
    $().each() 与 $.each()区别,以及 jquery ajax 应用
    每日一乐,健康多滋味~~
    IIS部署ASP.NET MVC (4.0)网站出现的错误
    《程序员级别鉴定书》 ----中级.NET开发者
    《转》程序员必须知道的10大基础实用算法及其讲解
    C# 托管资源和非托管资源
    11、E-commerce in Your Inbox:Product Recommendations at Scale-----产品推荐(prod2vec和user2vec)
    二叉树(2)----路径
    10、Latent Relational Metric Learning via Memory-based Attention for Collaborative Ranking-----基于记忆注意的潜在关系度量协同排序
  • 原文地址:https://www.cnblogs.com/wzc521/p/11271797.html
Copyright © 2011-2022 走看看