[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
#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>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]]

rp++

你可能感兴趣的:([CSP-S模拟测试]:树(树上LIS+主席树+线段树))