• [CSP-S模拟测试]:树(树上上升序列+主席树+线段树)


    题目传送门(内部题78)


    输入格式

      第一行输入两个整数$n,q$,表示节点数和询问数。
      第二行输入$n$个整数$w_i$,表示第$i$个点的智商。
      第三行至第$n+1$行每行输入两个数$x,y$,表示树上一条边。
      第$n+2$行至第$n+q+1$行每行三个数$u,v,c$表示一次探究。(保证$v$是$u$的祖先)


    输出格式

      输出$q$行,每行两个数表示探究过程中$cwystc$需要努力学习的次数。


    样例

    见下发文件


    数据范围与提示

      对于$10\%$的数据:$nleqslant 1,000$
      对于另外$30\%$的数据:家谱树为一条链
      对于$100\%$的数据:$n,q,w_i,cleqslant 100,000$


    题解

    转化一下题意,就是让我们求从$u$到$v$的上升序列长度(注意这里的上升序列是指碰见比它大的就选中,而不是最长上升子序列)。

    剩下就是码力问题了……

    我可能打的比较麻烦,用了主席树和线段树优化……

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

    期望得分:$100$分。

    实际得分:$100$分。


    代码时刻

    #include<bits/stdc++.h>
    #define L(x) x<<1
    #define R(x) x<<1|1
    using namespace std;
    struct rec{int nxt,to;}e[200000];
    int head[100001],cnt;
    int n,q;
    int w[100001];
    int son[100001],size[100001],top[100001],fa[100001],dfn[100001],rk[100001],tim,root[5000001],lson[5000001],rson[5000001],num[5000001],tot;
    int tr[400001];
    void add(int x,int y)
    {
    	e[++cnt].nxt=head[x];
    	e[cnt].to=y;
    	head[x]=cnt;
    }
    void pushups(int x){num[x]=num[lson[x]]+num[rson[x]];}
    void pushupm(int x){tr[x]=max(tr[L(x)],tr[R(x)]);}
    void build(int x,int l,int r)
    {
    	if(l==r){tr[x]=w[rk[l]];return;}
    	int mid=(l+r)>>1;
    	build(L(x),l,mid);
    	build(R(x),mid+1,r);
    	pushupm(x);
    }
    void adds(int &x,int f,int l,int r,int w)
    {
    	if(!x)x=++tot;
    	if(l==r){num[x]=1;return;}
    	int mid=(l+r)>>1;
    	if(w<=mid)
    	{
    		rson[x]=rson[f];
    		adds(lson[x],lson[f],l,mid,w);
    	}
    	else adds(rson[x],rson[f],mid+1,r,w);
    	pushups(x);
    }
    int asks(int x,int l,int r,int w)
    {
    	if(!x)return 0;
    	if(w<=l)return num[x];
    	int mid=(l+r)>>1;
    	if(w<=mid)return asks(lson[x],l,mid,w)+num[rson[x]];
    	else return asks(rson[x],mid+1,r,w);
    }
    int askm(int x,int l,int r,int L,int R)
    {
    	if(r<L||R<l)return 0;
    	if(L<=l&&r<=R)return tr[x];
    	int mid=(l+r)>>1;
    	return max(askm(L(x),l,mid,L,R),askm(R(x),mid+1,r,L,R));
    }
    void dfs(int x,int f)
    {
    	size[x]=1;
    	top[x]=x;
    	adds(root[x],root[f],0,1000000,w[x]);
    	for(int i=head[x];i;i=e[i].nxt)
    	{	
    		if(size[e[i].to])continue;
    		fa[e[i].to]=x;
    		dfs(e[i].to,x);
    		size[x]+=size[e[i].to];
    		if(size[son[x]]<size[e[i].to])son[x]=e[i].to;
    	}
    }
    void dfs(int x)
    {
    	dfn[x]=++tim;
    	rk[tim]=x;
    	if(son[x])
    	{
    		top[son[x]]=top[x];
    		dfs(son[x]);
    	}
    	for(int i=head[x];i;i=e[i].nxt)
    		if(!dfn[e[i].to])dfs(e[i].to);
    }
    int query(int x,int y,int c)
    {
    	int now=top[x],mx=c;
    	int res=asks(root[x],0,1000000,c+1);
    	while(now!=top[y])
    	{
    		mx=max(mx,askm(1,1,n,dfn[now],dfn[x]));
    		x=fa[now];
    		now=top[x];
    	}
    	mx=max(mx,askm(1,1,n,dfn[y],dfn[x]));
    	res-=asks(root[fa[y]],0,1000000,mx+1);
    	return res;
    }
    int main()
    {
    	scanf("%d%d",&n,&q);
    	for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    	for(int i=1;i<n;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		add(u,v);add(v,u);
    	}
    	dfs(1,0);
    	dfs(1);
    	build(1,1,n);
    	while(q--)
    	{
    		int u,v,c;
    		scanf("%d%d%d",&u,&v,&c);
    		printf("%d
    ",query(u,v,c));
    	}
    	return 0;
    }
    

    rp++

  • 相关阅读:
    check_mysql.sh
    shell 数组长度
    Shell脚本中计算字符串长度的5种方法
    非缓冲文件编程(实时操作)
    ferror,clearerr和EOF含义
    密码库生成
    筛选出多个数据并判断
    扫描有分隔符的数据
    unicode文件处理(如果是ANSI编码就不需要了)
    ferror,perror,cleaner
  • 原文地址:https://www.cnblogs.com/wzc521/p/11721940.html
Copyright © 2020-2023  润新知