[CSP-S模拟测试]:树(树上LIS+主席树+线段树)

题目传送门(内部题78)


输入格式

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


输出格式

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


样例

见下发文件


数据范围与提示

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


题解

转化一下题意,就是让我们求从$u$到$v$的$LIS$。

剩下就是码力问题了……

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

时间复杂度:$\Theta(n\log 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\n",query(u,v,c));
	}
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11721940.html

时间: 2024-08-30 14:01:55

[CSP-S模拟测试]:树(树上LIS+主席树+线段树)的相关文章

Codeforces 458C Elections 贿赂选票抢主席! 线段树

题目链接:点击打开链接 题意: 给定n张选票,每张选票有2个参数,第一个参数表示这张选票选的人 第二个参数表示如果让这张选票改为选0号 的花费 问:使得0号的选票是最高的(不能有和0号相同)的最小花费 枚举0号的最终选票 那么已知0号最终选票,则有些人选票比0号大的,那些票都要买下来. 如果买完了还是达不到 最终选票,就从所有剩下的选票里找前k小的. 用线段树求前k小的数的和,然后_(:зゝ∠)_就可以了 #include<iostream> #include<cstdio> #i

Codeforces 486E LIS of Sequence(线段树+LIS)

题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组,现在要确定每个位置上的数属于哪一种类型. 解题思路:先求出每个位置选的情况下的最长LIS,因为开始的想法,所以求LIS直接用线段树写了,没有改,可以用 log(n)的算法直接求也是可以的.然后在从后向前做一次类似LIS,每次判断A[i]是否小于f[dp[i]+1],这样就可以确定该位 置是否属于LIS序列.然后为第三类的则说明dp[i] = k的只有一个满足. #include <cstdio>

BZOJ 2243:染色(树链剖分+区间合并线段树)

[SDOI2011]染色Description给定一棵有n个节点的无根树和m个操作,操作有2类:1.将节点a到节点b路径上所有点都染成颜色c:2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”.请你写一个程序依次完成这m个操作.Input第一行包含2个整数n和m,分别表示节点数和操作数:第二行包含n个正整数表示n个节点的初始颜色下面 行每行包含两个整数x和y,表示x和y之间有一条无向边.下面 行每行描述一个操作:“C

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

[CSP-S模拟测试]:小P的单调数列(树状数组+DP)

题目描述 小$P$最近喜欢上了单调数列,他觉得单调的数列具有非常多优美的性质.经过小$P$复杂的数学推导,他计算出了一个单调增数列的艺术价值等于该数列中所有书的总和.并且以这个为基础,小$P$还可以求出任意一个数列的艺术价值,它等于将这个数列顺次划分若干个极长单调区间(相邻两个单调区间的单调性必须不相同)后,每个单调区间中元素总和的平均值.比如对于数列$3\ 7\ 9\ 2\ 4\ 5$,它将被划分为$[3\ 7\ 9]\ [2]\ [4\ 5]$,其艺术价值为$\frac{19+2+9}{3}

hdu4521 小明系列问题——小明序列(LIS变种 (线段树+单点更新解法))

链接: huangjing 题目:中文题目 思路: 这个题目如果去掉那个距离大于d的条件,那么必然是一个普通的LIS,但是加上那个条件后就变得复杂了.用dp的解法没有看懂,我用的线段树的解法...就是采用延迟更新的做法,用为距离要大于d啊,所以我们在循环到第i的时候,就对(i-d-1)这个点进行更新,因为如果在(i-d-1)这个点更新了,会对后面的造成影响,然后线段树的tree[]数组存的是以i结尾的最长lis,那么每次询问的时候就找最大的tree[]就可以了... 代码: 小明系列问题--小明

[XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动态开点. 线段树的节点维护两个值,一个是这段时间内的 1 操作个数,另一个是这段时间内变化的黑色节点权值和. 在处理所有操作的时候,每棵线段树都是仅代表树上的一个点,因此线段树的每个节点维护的就是这段时间内以这个点为 a 的 1 操作个数和这段时间内这个点的黑色节点权值和(这个点 x 由黑变白就 -

【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案

传送门 写在前面:虽然这是一道我再也不想写的题目,但很好很有价值 思路: cxlove大神: 要求中位数最大,首先二分中位数,然后判断可行不可行. 判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1.这样的话,求一次最大区间和 如果大于等于0,则说明可行. 这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树 具体实现 对于题目