「CSP-S模拟赛」2019第一场


这场考试感觉很奇怪。
T1、T2T1、T2T1T2 都缺一个小特判。
T3T3T3 打了个比暴力优的暴力 还是暴力,但是不知道为什么 WAWAWA 穿了。
考试的时候还玩扫雷…
其实,菜是原罪啊…


T1 小奇取石子

题目

点这里

考场思路

刚开始差点被自己坑了,开考 5min5min5min 就码出了一个可以惨痛爆零的 010101 背包。
结果还好的是交卷前几分钟自己出了个小数据卡掉自己,然后就码出一个 80pts80pts80pts 的代码。
首先根据数据将方法分开。
对于 A、BA、BAB 两组数据,很明显暴力都可以过,这个没有问题。
对于 CCC 组数据,定义 dp[i]dp[i]dp[i]:得到石子数为 iii 时的最小选择石子堆。
状转见代码。

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
	int x=0,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
}
template<class T>inline void qread(T& x){
	x=0;char c;bool f=0;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=200;
const int MAXK=2500;
const int INF=0x3f3f3f3f;

int n,m,k,a[MAXN+5],maxx;
int dp[MAXK+5];

inline void init(){
	qread(n,m,k);
	for(int i=1;i<=n;++i)qread(a[i]);
}

void dfs(const int i,const int x,const int w){
	if(w>k||x>m)return;
	maxx=Max(maxx,w);
	if(i>n)return;
	dfs(i+1,x+1,w+a[i]);
	dfs(i+1,x,w);
}

inline void getDp(){
	rep(i,1,k)dp[i]=INF;
	rep(i,1,n)rep(j,a[i],k)dp[j]=Min(dp[j],dp[j-a[i]]+1);
	dep(i,k,1)if(dp[i]<=m){maxx=i;break;}
}

signed main(){
	// freopen("stone.in","r",stdin);
	// freopen("stone.out","w",stdout);
	init();
	if(n<=20)dfs(1,0,0);
	else getDp();
	printf("%d\n",maxx);
	return 0;
}

正解

其实就是我的考场代码改个细节,为了保存上一个 iii 的状态,jjj 应该从大到小枚举
不知道我考场的时候脑子 what 了,这个细节都打错了…

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
	int x=0,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
}
template<class T>inline void qread(T& x){
	x=0;char c;bool f=0;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=200;
const int MAXK=2500;
const int INF=0x3f3f3f3f;

int n,m,k,a[MAXN+5],maxx;
int dp[MAXK+5];

inline void init(){
	qread(n,m,k);
	for(int i=1;i<=n;++i)qread(a[i]);
}

void dfs(const int i,const int x,const int w){
	if(w>k||x>m)return;
	maxx=Max(maxx,w);
	if(i>n)return;
	dfs(i+1,x+1,w+a[i]);
	dfs(i+1,x,w);
}

inline void getDp(){
	rep(i,1,k)dp[i]=INF;
	rep(i,1,n)dep(j,k,a[i])dp[j]=Min(dp[j],dp[j-a[i]]+1);
	dep(i,k,1)if(dp[i]<=m){maxx=i;break;}
}

signed main(){
	// freopen("stone.in","r",stdin);
	// freopen("stone.out","w",stdout);
	init();
	if(n<=20)dfs(1,0,0);
	else getDp();
	printf("%d\n",maxx);
	return 0;
}

T2 「CCO 2017」专业网络

题目

点这里

考场思路

所谓信息竞赛,其实就是面向数据编程

看看数据范围,发现前两组很好骗分,然后就可以对于这两组数据进行骗分了…

#include<cstdio>
#include<algorithm>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
using namespace std;
#define cg (c=getchar())
inline int qread(){
	int x=0,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
}
template<class T>inline void qread(T& x){
	x=0;char c;bool f=0;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;
const int INF=0x3f3f3f3f;

int N,maxb,ans=INF;
struct node{
	int a,b;
	inline void input(){qread(a,b);if(a==0)b=0;}
	node(){}
	node(const int A,const int B):a(A),b(B){}
	bool operator<(const node t){return b==t.b?a<t.a:b<t.b;}
}p[MAXN+5];

inline void init(){
	qread(N);
	rep(i,1,N)p[i].input(),maxb=Max(maxb,p[i].b);
}

inline void dfs30(const int,const int,const int);
inline bool cmp30(const node _x,const node _y){
	return _x.a==_y.a?_x.b<_y.b:_x.a<_y.a;
}
inline void pts30(){
	sort(p+1,p+N+1,cmp30);
	dfs30(0,0,0);
}

inline void dfs30(int tot,int state,const int cost){
	if(tot==N)return (void)(ans=Min(ans,cost));
	if(cost>ans)return;

	rep(i,1,N)if(!(state&(1<<(i-1)))&&tot>=p[i].a)++tot,state|=(1<<(i-1));

	if(tot==N)return (void)(ans=Min(ans,cost));

	rep(i,1,N)if(!(state&(1<<(i-1))))
		dfs30(tot+1,state|(1<<(i-1)),cost+p[i].b);
}

inline bool cmp15(const node _x,const node _y){
	return _x.a>_y.a;
}
inline void pts15(){
	sort(p+1,p+N+1,cmp15);
	int l=1,r=N,tot=0;ans=0;
	while(l<=r){
		while(p[r].a<=tot)--r,++tot;
		if(r<l)break;
		if(p[l].a>tot)++ans;
		++l,++tot;
	}
}

signed main(){
	// freopen("network.in","r",stdin);
	// freopen("network.out","w",stdout);
	init();
	if(maxb==1)pts15();
	else pts30();
	printf("%d\n",ans);
	return 0;
}

题解

其实,这道题跟 《信息奥赛一本通》中的不守交规 有异曲同工之妙。——JZM\text{JZM}JZM 大佬

首先,我们跟每一个人建立友谊关系的状态肯定唯一。
假如我们跟 iii 交朋友之前,我们已经交了 jjj 个朋友。
那么,肯定不存在 k(k≠i)k(k\neq i)k(k=i) 使得我们在与 kkk 交朋友之前已经交了 jjj 个朋友。
所以,我们的 jjj 可以取 [0,N−1][0,N-1][0,N1] 之中的数。
那么,我们只需要在交这 NNN 个朋友的时候,将他们对应到这 [0,N−1][0,N-1][0,N1]NNN 个数中去即可。
而如果 iii 是不用花费的,那么它的对应值一定在 [Ai,N−1][A_i,N-1][Ai,N1] 中的一个数。
贪心地,我们首先要满足那些 BiB_iBi 较大的数,这样我们的花费就会尽可能的少。
而为了让区间能够对应的数尽可能多,我们的搜索需要从 AiA_iAi 开始枚举。
而对于一个 iii,如果在 [Ai,N−1][A_i,N-1][Ai,N1] 中已经没有数没有被对应到,那么它就一定需要支付费用。
那么这一段的代码也就很好写了

for(int i=1,loc;i<=N;++i){
	bool f=true;
	for(int j=p[i].a;j<N;++j)if(!vis[j]){
		vis[j]=true,f=false;
		break;
	}
	if(!f)ans+=p[i].b;
}

然而这是 O(N2)O(N^2)O(N2) 的算法,它最多只能得到 60pts60pts60pts,因此我们需要用到并查集优化。
时间复杂度接近于 O(N)O(N)O(N),我省掉了反阿克曼函数,它增长地太慢了。

#include<cstdio>
#include<algorithm>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
using namespace std;
#define cg (c=getchar())
inline int qread(){
	int x=0,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
}
template<class T>inline void qread(T& x){
	x=0;char c;bool f=0;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;

int N,ans,pre[MAXN+5];bool vis[MAXN+5];
struct node{
	int a,b;
	inline void input(){qread(a,b);}
	node(){}
	node(const int A,const int B):a(A),b(B){}
	bool operator<(const node t){return b==t.b?a<t.a:b>t.b;}
}p[MAXN+5];

inline void init(){
	qread(N);
	for(int i=1;i<=N;++i)p[i].input();
	sort(p+1,p+N+1);
}

inline int findSet(const int u){
	return u==pre[u]?u:pre[u]=findSet(pre[u]);
}

inline void calc(){
	for(int i=1;i<=N;++i)pre[i]=i;
	for(int i=1,loc;i<=N;++i){
		loc=findSet(p[i].a);
		if(loc>=N)ans+=p[i].b;
		else pre[loc]=loc+1;
	}
}

signed main(){
	init();
	calc();
	printf("%d\n",ans);
	return 0;
}

T3 「ZJOI2017」线段树

题目

点这里

考场思路

考试的时候,我想到一种时间复杂度只有 O(nm)O(nm)O(nm) 的暴力算法 虽然还是暴力,但是时间少了点
但是我没有仔细思考其正确性。
大致思路是这样的:
在询问区间 [l,r][l,r][l,r] 的时候,假如有某一段区间 [l′,r′][l',r'][l,r]uuu 不在大树的同一棵子树上,那么就计算区间 [l′,r′][l',r'][l,r] 的贡献。
但是这样做有问题,假若区间 [l′,r′′][l',r''][l,r] 被包含于 [l,r][l,r][l,r] ,并且 r′r'rr′′r''r 不在同一棵子树内,那么我的算法就会使得答案 ansansans 变大。
因为这样做会使我寻找的可以被拼成区间 [l,r][l,r][l,r] 的点变多,自然而然,ansansans 也就变大了。
附个代码

#include<cstdio>
#define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define dep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define cg (c=getchar())
inline int qread(){
	int x=0,f=1;char c;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	return x*f;
}
template<class T>inline void qread(T& x){
	x=0;char c;bool f=0;
	while(cg<'0'||'9'<c)if(c=='-')f=-1;
	for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
	if(f)x=-x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
#undef cg
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}

const int MAXN=2e5;

struct node{
	int l,r,mid,d,lc,rc;
	node(){lc=rc=0;}
	node(const int L,const int R,const int M,const int D):l(L),r(R),mid(M),d(D){lc=rc=0;}
}tre[(MAXN<<1)+5];

int N,q,ncnt,ans;
int s[(MAXN<<1)+5],tail;

inline int buildtre(const int pre,const int l,const int r,const int d){
	int now=++ncnt;
	if(l==r)return tre[now]=node(l,r,0,d),now;
	tre[now]=node(l,r,qread(),d);
	tre[now].lc=buildtre(now,l,tre[now].mid,d+1);
	tre[now].rc=buildtre(now,tre[now].mid+1,r,d+1);
	return now;
}

inline void init(){
	qread(N);
	buildtre(0,1,N,1);
}

void getNode(const int i,const int l,const int r){
	if(l<=tre[i].l&&tre[i].r<=r)return (void)(s[++tail]=i);
	if(l<=tre[i].mid)getNode(tre[i].lc,l,r);
	if(r>tre[i].mid)getNode(tre[i].rc,l,r);
}

inline void calc(const int lca,const int u){
	while(tail>0)ans+=tre[s[tail--]].d+tre[u].d-2*tre[lca].d;
}

void algor(const int i,const int u,const int l,const int r){

	// printf("algor : %d %d %d %d\n",i,u,l,r);

	if(u==i){
		getNode(i,l,r);
		calc(i,i);
		return;
	}

	if(tre[i].lc<=u&&u<tre[i].rc&&r<=tre[i].mid)algor(tre[i].lc,u,l,r);
	else if(u>=tre[i].rc&&l>tre[i].mid)algor(tre[i].rc,u,l,r);
	//以上皆为同方向的子树时
	else{
		//在不同向
		//此时一定可以保证, i 即为他们的 lca
		if(u>=tre[i].rc&&l<=tre[i].mid){//当询问区间的 部分/全部 在左边, 点在右边时
			getNode(tre[i].lc,l,Min(tre[i].mid,r));
			calc(i,u);
			if(r>tre[i].mid)algor(tre[i].rc,u,tre[i].mid+1,r);
		}
		else if(tre[i].lc<=u&&u<tre[i].rc&&r>tre[i].mid){//当询问区间的 部分/全部 在右边, 点在左边时
			getNode(tre[i].rc,Max(tre[i].mid+1,l),r);
			calc(i,u);
			if(l<=tre[i].mid)algor(tre[i].lc,u,l,tre[i].mid);
		}
		/*
		else if(u==i){//当点就是当前的点的时候, 直接在其子树中寻找区间的点进行计算
			getNode(i,l,r);
			calc(i,i);
		}
		*/
	}
}

inline void getQuery(){
	int q=qread(),u,l,r;
	while(q--){ans=0;
		qread(u,l,r);
		algor(1,u,l,r);
		printf("%d\n",ans);
	}
}

signed main(){
	// freopen("0.in","r",stdin);
	// freopen("tree.out","w",stdout);
	init();
	// puts("finished input!");
	getQuery();
	return 0;
}

正解

一道编码较为困难的数据结构题。
首先,我们解决询问的区间的问题。
看下面这张图
在这里插入图片描述
其实这个就是题目描述里面的那张图。
假设我们需要访问区间 [2,4][2,4][2,4],应该怎么做呢?
方法一
你可以肉眼看…虽然这样好像不能交到 OJOJOJ 上去…
但是我们可以看出我们要找的节点是 (2−3)(2-3)(23)(4−4)(4-4)(44)
方法二
使用类似于普通线段树的方法进行区间查找,这样的复杂度对于这样的广义线段树来说大概是 O(n)O(n)O(n) 的。
方法三
可以用类似于 zkw\text{zkw}zkw 线段树 的方式。
我们要访问区间 [2,4][2,4][2,4] ,那么左边从 (1−1)(1-1)(11) 开始,右边从 (5−5)(5-5)(55) 开始,一起往上爬。
如果左边点往上爬,发现它是父亲节点的左儿子,那么其父节点的右儿子是一定是我们要找的点。
如果右边点往上爬,发现他是父节点的右儿子,那么其父节点的左儿子是一个是我们要找的点。
大概搜索的结果就是在这里插入图片描述
其中,被红色笔圈起来的点是我们要特殊注意的,而蓝色下划线是我们要取到的点。
那么这样做的时间复杂度?不用说,O(n)O(n)O(n)


先把这些方法放在一边,看一看我们需要求什么。
题目似乎给出ans=∑v∈S[l,r]dis(u,v)ans=\sum_{v\in S[l,r]}dis(u,v)ans=vS[l,r]dis(u,v)d[u]d[u]d[u]:点 uuu 的深度。
我们可以将 dis(u,v)dis(u,v)dis(u,v) 换成用 lcalcalca (时间复杂度 O(logn)O(logn)O(logn) 左右,不要忽略了)来表示,那么就有ans=∑v∈S[l,r]d[u]+d[v]−2⋅d[lca(u,v)]ans=\sum_{v\in S[l,r]}d[u]+d[v]-2\cdot d[lca(u,v)]ans=vS[l,r]d[u]+d[v]2d[lca(u,v)]假设我们最后可以处理出,我们找到的满足 v∈S[l,r]v\in S[l,r]vS[l,r] 的点共有 ttt 个,那么这个公式可以再展开:ans=t⋅d[u]+∑v∈S[l,r]d[v]−∑v∈S[l,r]2⋅d[lca(u,v)]ans=t\cdot d[u]+\sum_{v\in S[l,r]}d[v]-\sum_{v\in S[l,r]}2\cdot d[lca(u,v)]ans=td[u]+vS[l,r]d[v]vS[l,r]2d[lca(u,v)]然而这个式子似乎再也不能往下化简了。


我们再往回看,我们已分析出的两种方法 人为忽略第一种
假若我们用 方法二 ,那么我们似乎并不能做什么优化,只有用标准的线段树做法,时间复杂度还是 O(n)O(n)O(n)
假若我们用 方法三 ,那么我们似乎可以用树上差分
怎么个差分法呢?
记录一下每个点的信息

  • tls[u]tls[u]tls[u]:从根到 uuu 一共有多少左儿子是没有经过的
  • trs[u]trs[u]trs[u]:从根到 uuu 一共有多少右儿子是没有经过的
  • tdls[u]tdls[u]tdls[u]:从根到 uuu 一共没见过的左儿子的深度之和
  • tdrs[u]tdrs[u]tdrs[u]:从根到 uuu 一共没见过的右儿子的深度之和

那么我们怎么求以上内容呢?
可以在建树的时候顺便处理出来。
假设我们有一个点 fafafa,其深度为 ddd,它的左儿子是 lclclc,右儿子是 rcrcrc
那么,若 rcrcrc 存在,且 lclclc 存在,则满足
trs[lc]=trs[fa]+1,tdrs[lc]=tdrs[fa]+(d+1),tls[lc]=tls[fa],tdls[lc]=tdls[fa]trs[lc]=trs[fa]+1,tdrs[lc]=tdrs[fa]+(d+1),tls[lc]=tls[fa],tdls[lc]=tdls[fa]trs[lc]=trs[fa]+1,tdrs[lc]=tdrs[fa]+(d+1),tls[lc]=tls[fa],tdls[lc]=tdls[fa]而若 rcrcrc 不存在,则有
trs[lc]=trs[fa],tdrs[lc]=tdrs[fa],tls[lc]=tls[fa],tdls[lc]=tdls[fa]trs[lc]=trs[fa],tdrs[lc]=tdrs[fa],tls[lc]=tls[fa],tdls[lc]=tdls[fa]trs[lc]=trs[fa],tdrs[lc]=tdrs[fa],tls[lc]=tls[fa],tdls[lc]=tdls[fa]如果 lclclc 存在,且 rcrcrc 存在,则满足
trs[rc]=trs[fa],tdrs[rc]=tdrs[fa],tls[rc]=tls[fa]+1,tdls[rc]=tdls[fa]+(d+1)trs[rc]=trs[fa],tdrs[rc]=tdrs[fa],tls[rc]=tls[fa]+1,tdls[rc]=tdls[fa]+(d+1)trs[rc]=trs[fa],tdrs[rc]=tdrs[fa],tls[rc]=tls[fa]+1,tdls[rc]=tdls[fa]+(d+1)然后,我们就可以在建树,或者是输入建树时预处理出以上内容即可。
接着之前说的树上差分
假若我们要求区间 [a,b][a,b][a,b] ,那么我们就从 (a−1,a−1)(a-1,a-1)(a1,a1)(b+1,b+1)(b+1,b+1)(b+1,b+1) 开始往上爬。
找到它们的最近公共祖先 lcalcalca,那么我们要求的公式中的 t、∑v∈S[l,r]d[v]t、\sum_{v\in S[l,r]}d[v]tvS[l,r]d[v] 都可以用树上差分解决。
ttt 满足
t=trs[leafu[a−1]]−trs[lca]+tls[leafu[b+1]]−tls[lca]t=trs[leafu[a-1]]-trs[lca]+tls[leafu[b+1]]-tls[lca]t=trs[leafu[a1]]trs[lca]+tls[leafu[b+1]]tls[lca]∑v∈S[l,r]d[v]\sum_{v\in S[l,r]}d[v]vS[l,r]d[v] 满足
∑v∈S[l,r]d[v]=tdrs[leafu[a−1]]−tdrs[lca]+tdls[leafu[b+1]]−tdls[lca]\sum_{v\in S[l,r]}d[v]=tdrs[leafu[a-1]]-tdrs[lca]+tdls[leafu[b+1]]-tdls[lca]vS[l,r]d[v]=tdrs[leafu[a1]]tdrs[lca]+tdls[leafu[b+1]]tdls[lca]似乎以上部分都是可以使用 O(log)O(log)O(log) (寻找 lcalcalca)来 O(1)O(1)O(1) 地解决问题,但是 ansansans 还有一个部分:
−∑v∈S[l,r]2⋅d[lca(u,v)]-\sum_{v\in S[l,r]}2\cdot d[lca(u,v)]vS[l,r]2d[lca(u,v)]这个部分能否使用树上差分呢?
答案是:肯定不行。
为什么?因为 vvv 在改变时,lca(u,v)lca(u,v)lca(u,v) 也在跟着改变。
那么怎么做?
分类讨论 uuu 的位置,这里就和我的暴力思路有点相像。
分以下几类:
先假设以 leafu[a−1]、leafu[b+1],lcaleafu[a-1]、leafu[b+1],lcaleafu[a1]leafu[b+1],lca 围成的树为 tretretre
flca()flca()flca() 为寻找 lcalcalca 的算法。

  • uuutretretre 之外时。这样又要分两类
    - 当 uuulcalcalca 的祖先,那么一定满足∀v∈S[l,r],flca(u,v)=u\forall v\in S[l,r],flca(u,v)=uvS[l,r],flca(u,v)=u
    - 当 uuu 不是 lcalcalca 的祖先,那么一定满足∀v∈S[l,r],flca(u,v)=lca(lca,u)\forall v\in S[l,r],flca(u,v)=lca(lca,u)vS[l,r],flca(u,v)=lca(lca,u)
  • uuutretretre 之内时,对于这种情况需要自行推理。。。

另注:
可以发现,如果我们就这样建树的话,如果询问区间 [1,n][1,n][1,n] 之类的区间就会出现问题,那么怎么解决?其实可以这样建图(以题目的树为例)
在这里插入图片描述
代码

没时间补题啊
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值