一、dp:
1.Codeforces Round #613 (Div. 2)D
2.Codeforces Round #612 (Div. 1)A
题意:给出三个数分别代表红、绿、蓝,问给的数量是否能组成两两颜色不相邻的圆环。
3.Codeforces Round #614 (Div. 2)E
题意:给出一棵n个点的树,将0—n-2作为边权,最大化 Σ mex(u,v)
mex(u,v) 表示u到v的路径上最小的未出现过的自然数
【题解】
补充:
题解中的S的值是由更小区间获取的状态的转移、路径两边的结点数共同决定。 就是在该线段填完后,要在左边填下一个数还是右边(用搜索式的dp)。
4.2020牛客寒假算法基础集训营1I nico和niconiconi
题目大意:
nico也想当一回计数菌。她认为:“nico” 计a分,“niconi” 计b分,“niconiconi” 计c分。
她拿到了一个长度为n的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+“nico"计2a分,要么当作"niconi”+"co"计b分。
状态转移方程
5.H 施魔法
题意:牛可乐有 n 个元素( 编号 1…n ),第 i 个元素的能量值为
a
i
a_i
ai
牛可乐可以选择至少 k 个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为 S,则消耗的魔力为
max
i
∈
S
{
a
i
}
−
min
i
∈
S
{
a
i
}
\max_{i\in S}\{a_i\}-\min_{i\in S}\{a_i\}
maxi∈S{ai}−mini∈S{ai}
牛可乐要求每个元素必须被使用恰好一次。牛可乐想知道他最少需要多少魔力才能用完所有元素
思路:
6.D. Minimax
题目大意:
选定一个元素删除其所在行所在列把图分为四部分,求一个点分割后的四部分每一部分的最大值,取出来得到四个,这四个求一下最大最小的差,让这个差尽可能的小
题解:预处理一下4个方向,枚举每个位置即可(除了边界),注意数组清0。很容易想到递推式
7.F. Jon and Orbs
题意:
题意:琼恩打败白色的步行者需要k中能量水晶,但是这k种水晶并不是按照一定顺序出现的,而是随机出现,并且每种水晶出现的概率相同,琼恩想知道让每种水晶都至少有一件的概率大于P/2000至少需要多少天,共有q次查询
思路:
当时没仔细看这道题,才发现这道题是简单的概率dp。
dp[ i ] [ j ]= (dp[ i-1 ][ j ] * j / k)+( dp[ i-1 ][ j-1 ] * ( k-j+1 )/k)
8.碎碎念
题意:
给你一个x,当wa的时候他会叫x声wa,当AC的时候叫一声AC,问当听到可能l到r次声音。问这个人可能遇到多少种情况。(每次wa后下一次一定是AC)
思路:
dp[i][2] 第i声叫是:0为ac,1为wa
递推式:
初始化dp[0][0]=1;
dp[i][1]=dp[i-x][0]; //wa前面一定是ac
dp[i][0]=dp[i-1][1]+dp[i-1][0]; //ac可以由前面ac和wa转化
然后求一次前缀和。
sum[i]=sum[i-1]+dp[i][0]+dp[i][1];
按区间求值即可。
9.Moortal Cowmbat
题意:
给你一个字符串,和一个MM(2626)的表,表示将i转化为j的代价。问将字符串转化为每个字母至少连续k次的字符串,可以做任意次修改。求最小代价。
思路:
首先很容易想到dp
假设dp[i][j]为第i个位置为j且前面已经满足至少k个的状态
状态转移
if(j==k) dp[i+1][k]=min(dp[i+1][k],dp[i][k],val(i+1,k))
dp[i+k]=min(dp[i+k][k],dp[i][j]+val(i->i+k,k))
其次,因为可以进行任意次操作,所以有可能要从转换好几次到才到这个字母。
所以对表求一次floyd。之后求一次前缀和即可。
10.迷宫
题意:
思路:
之后dp就不难想了。
11.Snakes
题意:
给你n个大小为ai的物品,你可以自己修改k次袋子的大小,袋子每次装一个,空出的算浪费值。为你按顺序装物品,最小的浪费值。
思路:
dp[i][k]到第i位置,且为第k个袋子为结尾。
递推式
dp[i][k]=min(dp[i][k],dp[j][k-1]+maxs[j+1][i]*(i-j)-(sum[i]-sum[j]));
12.E. Sleeping Schedule
题意:
给你一个范围l,r。
给你一组数a[i]
开始为0,每次都可以+a[i]或者+a[i]-1。
问最多可以多少个在l,r中。
思路:
dp
很多种写法:
1.前缀和
for(int i=1;i<=n;i++){
sum+=a[i];
for(int j=0;j<=i;j++){
dp[i][j]=max(j?dp[i-1][j-1]:0,dp[i-1][j]);
int tmp=(sum-j)%h;
if(l<=tmp&&tmp<=r) dp[i][j]++;
}
}
2.更新dp[i][j]
memset(dp,-0x3f3f3f3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=0;j<h;j++){
dp[i][j]=max(dp[i-1][(j+h-a[i])%h],dp[i-1][(j+h-(a[i]-1))%h]);
if(dp[i][j]>=0&&l<=j&&j<=r) dp[i][j]+=1;
}
}
3.更新dp[i][(j + a[i]) % h]
memset(dp,-0x3f3f3f3f,sizeof(dp));
dp[0][0]=0;
int now = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < h; ++j) {
dp[i][(j + a[i]) % h] = max(dp[i][(j + a[i]) % h],
dp[i - 1][j] + check((j + a[i]) % h));
dp[i][(j + a[i] - 1) % h] =
max(dp[i][(j + a[i] - 1) % h],
dp[i - 1][j] + check((j + a[i] - 1) % h));
}
}
二、greedy:
1.Codeforces Round #611 (Div. 3)E
题意:一些人住在一些房子里,每个人最多移动一次,问你房子的最大最小数量
三、think:
1.Camp day1 最大公约数
2.J. Alyona and Spreadsheet
题意:第一行输入两个整数n,m(1 ≤ n·m ≤ 100 000),第二行输入一个n×m的矩阵,然后再输入一个整数k,接下来k行,每行两个整数r,l,
询问从r到l行,如果存在一列数是非递减的,就输出Yes,否则就输出No。
反思:
类似的二维问题,更多是压缩成一维来考虑。
还有考虑的顺序也会对解题有一定的影响。如果从下到上不行,就从上到下。如果一次性加入数组不行,就边加边求值。
3.Codeforces Round #615 (Div. 3) E. Obtain a Permutation
题目大意:
两种操作,一种将一个数换成另一个,一个是把一列当成转盘总体往前移一格。问最少多少步能走到要求的矩阵。
反思:
以后遇到这种类似转盘123一步变成312的,可以通过取模,然后计有多少个1步成功,2步成功,之后跑一次for取min就可以了。
4.拿物品
题意:物品编号为
1
,
2
,
…
,
n
1,2,\dots,n
1,2,…,n,每个物品有两个属性
a
i
,
b
i
a_i, b_i
ai,bi
轮流从剩下物品中任意拿走一个
得分为
∑
i
∈
H
a
i
\sum_{i\in H}{a_i}
∑i∈Hai而 牛可乐得分为
∑
i
∈
T
b
i
\sum_{i\in T} b_i
∑i∈Tbi
思路:
5. I 建通道
题目:
在无垠的宇宙中,有 n 个星球,第 i 个星球有权值
v
i
v_i
vi。
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是
lowbit
(
v
i
⊕
v
j
)
\text{lowbit}(v_i\oplus v_j)
lowbit(vi⊕vj),其中
⊕
\oplus
⊕ 为二进制异或,而
lowbit
(
x
)
\text{lowbit}(x)
lowbit(x)为 x 二进制最低位 1 对应的值,例如
lowbit
(
5
)
=
1
,
lowbit
(
8
)
=
8
\text{lowbit}(5)=1,\text{lowbit}(8)=8
lowbit(5)=1,lowbit(8)=8。特殊地,
lowbit
(
0
)
=
0
\text{lowbit}(0)=0
lowbit(0)=0。
牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。
思路:
首先将权值去重(权值相等的点连接代价为 00 ),设去重后有 mm 个点,接下来找到最小的二进制位 kk ,满足存在
v
i
v_i
vi的这个二进制位是 00 且存在
v
j
v_j
vj的这个二进制位是 1 ,答案就是
2
k
×
(
m
−
1
)
2^k\times (m-1)
2k×(m−1)(相当于所有这位是 00 的点与 jj 点连边,是 11 的点与 ii 点连边)。
排序和去重以外时间复杂度
O
(
n
+
log
V
)
O(n+\log V)
O(n+logV),没有卡
O
(
n
log
V
)
O(n\log V)
O(nlogV) ,好像两个
log
\log
log 也过了。
6.F. Complete the Projects (easy version)
题目大意:给出n个任务,每个任务有
r
a
t
i
n
g
rating
rating限制和加分
s
c
o
r
e
i
score_{i}
scorei
s
c
o
r
e
i
score_{i}
scorei可能是个负数),给出你的初始rating,当
r
a
t
i
n
g
>
r
a
t
i
n
g
i
rating>rating_{i}
rating>ratingi
时,才能完成第i个任务,完成任务后:
r
a
t
i
n
g
=
r
a
t
i
n
g
+
s
c
o
r
e
i
rating=rating+score_{i}
rating=rating+scorei,你可以任意安排完成任务的顺序,且完成任意一个任务后必须满足
r
a
t
i
n
g
>
=
0
rating>=0
rating>=0,若能实现输出YES否则输出NO。
思路:
当score是正的时候很好处理,从小到大排序
r
a
t
i
n
g
i
rating_i
ratingi即可。
当score是负的时候:
若1:
x
1
,
y
1
x_1,y_1
x1,y1,2:
x
2
,
y
2
x_2,y_2
x2,y2
如果一定要先选1,不能先选2,则可以推出不等式:
1.rating>=x1
2.rating+y1>=x2 (y1为负数)
3.rating>=x2
4.rating+y2<=x1 (y2为负数)
合并2,4可得
y1+x1>=y2+x2
所以排序按
s
c
o
r
e
i
+
r
a
t
i
n
g
i
score_i+rating_i
scorei+ratingi排序即可。
7.B 牛牛的DRB迷宫II
题目:
8.F 树上博弈
题意:
现有一个 n 个点,n-1条边组成的树,其中 1 号点为根节点。
牛牛和牛妹在树上玩游戏,他们在游戏开始时分别在树上两个不同的节点上。
在游戏的每一轮,牛牛先走一步,而后牛妹走一步。他们只能走到没有人的空节点上。如果谁移动不了,就输掉了游戏。现在牛牛和牛妹决定随机选择他们分别的起点,于是他们想知道,有多少种游戏开始的方式,使得牛牛存在一种一定获胜的最优策略。
两种开始方式相同,当且仅当在两种开始方式中牛牛,牛妹的开始位置是分别相同的,否则开始方式就被视作不同的。
思路:
很容易想到,当他们间隔为奇数时,牛牛必胜。
收获:可以通过计算深度,来知道从一个节点到另一个节点到距离是奇数还是偶数。
9. G 音乐鉴赏
题意:作为“音乐鉴赏”课的任课老师,你的课程作为刷学分好课一直受到广泛欢迎。但这一学期,学校制定了新的标准,你的课的优秀率(分数超过90分的人数)被限制在10%以下!
为了应对这个调整,你要求所有的同学都写了一篇论文,并使用随机算法打出了0-90之间的分数,分数可能不是整数。这里的随机是指,对于在[0,90]这个闭区间上的任何一对等长的区间,分数出现在其中的概率均是相同的。在期末的分数占比为百分之多少的时候,你的课程优秀率期望恰好在10%?保证所有同学的平时成绩都高于90分。
思路:
挺有意思的一道题。
10.D. Fill The Bag
题意:
给你一个n大小的包,和m个为2次幂大小的盒子。盒子的可以拆分为两个相同的盒子。问你最少几次拆分,就可以把包填满。
思路:
其实可以从大往小取,当盒子比包剩余空间小的时候直接取,当不取这个但是剩下的又不够时,就得分这个盒子,当不取这个且剩下的还够的时候,就可以跳过这个盒子。
11.D. Shortest and Longest LIS
题意:给你n-1个>和<号,问你怎么排列1-n的数,使得单调递增子序列最长和最短。
思路:
最长一定是12345的变形,所以遇到>调换位置
最短一定是54321的变形,所以遇到<调换位置
12.C. Cow and Message
题意:求字符串中一个字符或者两个字符组成的字符串出现的子序列的次数。
思路:
求两个的时候,可以类似树状数组的操作。统计前面加入了多少个i字符,然后每次到一个位置,这个位置的x都都更新一次与其他字符的对数。
13.C2. Skyscrapers (hard version)
题意:
给你一个序列a,
a
i
a_i
ai代表在i最高可以建立
a
i
a_i
ai高的楼。但是每个楼都要求左边或右边不能同时有比它高的楼。求所有楼总和最大。
思路:
我们可以预处理以从左边每个i能跑的最高距离和,和右边跑每个i能跑最高距离和。然后枚举i即可。
14.D. Concatenated Multiples
题意:给出n个数,求数对(x,y)的个数,使得ax和ay简单拼接后能被kk整除。
思路:
对于两个数x和y,简单拼接后能被k整除,当且仅当
x
×
10
l
o
g
10
y
+
1
m
o
d
k
+
y
m
o
d
k
=
=
0
x×10log10y+1mod k + y mod k == 0
x×10log10y+1modk+ymodk==0
所以可以预处理出每个数ai后填上j个0除k的余数x,用map存储添j个0余数为x的数的个数,计作mp[j][x]。
预处理完,就再对于每个数查询满足上述条件的数的个数,加入答案中。
注意当一个数自己和自己拼接可以被k整除时要特殊判断下。
15.矩阵消除游戏
题意:
矩阵的大小是n行m列,第i行第j列的单元格的权值为
a
i
,
j
a_{i,j}
ai,j ,可以进行k个回合,在每个回合,选择一行或者选择一列,然后将这一行或者这一列的所有单元格中的权值变为0,同时分数会加上这一行或者这一列中的所有单元格的权值的和。
思路:
16.E. 卖水果
题意:
有𝑛个水果,A想用𝑎𝑖元去买第𝑖个水果,B想用𝑏𝑖元去买第𝑖个水果。Tom最多想要买n个水果,而Jim最多想要买m个水果。每一个水果只能够被卖给他们中的一个人。他最多能赚多少钱。
思路:
先按a从大到小排序。
l
[
i
]
l[i]
l[i]的定义:前面选择A-1个a且最后一个取a[i]的最大赚钱量。(因为要满足选择满A个,所以i从A开始)
i
=
A
+
B
1
i=A+B_1
i=A+B1
这里假设我们取了前A个。那么
l
[
A
]
=
s
u
m
(
a
[
1
]
.
.
.
a
[
A
]
)
l[A]=sum(a[1]...a[A])
l[A]=sum(a[1]...a[A]),并将每个b-a丢入优先队列中
然后每到后面我们+a[i]并减去优先队列里b-a最小的
(因为当我们不选
a
[
k
]
a[k]
a[k]时,那我们一定会去选
b
[
k
]
b[k]
b[k],不然我们不会凭空放弃掉
a
[
k
]
a[k]
a[k]。 )
所以我们可以算出
l
[
A
.
.
.
A
+
B
]
l[A...A+B]
l[A...A+B]。
即l[i]左边i全部选满的最大赚钱数
r
[
i
]
r[i]
r[i]就是从i开始右边选出
B
+
A
−
i
B+A-i
B+A−i赚最多的钱
之后我们就要算剩下的除去上面选满里面包括的
B
1
B_1
B1,我们还有
B
−
B
1
B-B_1
B−B1其实就是剩下
B
+
A
−
i
个
选
B
的
B+A-i个选B的
B+A−i个选B的
所以我们就要算出i开始右边能选出最大的赚钱数(选b且数量为B+A-i)。
这个可以倒序算出。
所以ans=max(l[i]+r[i])
16.最大GCD
题意:
思路:
很容易想到只要找到l到r中一个数和x的gcd最大即可
那么gcd一定是x的因数。所以这里可以考虑枚举x的因数。
其次就是在这个区域内有没有含有这个因数的值。
所以我们可以把有相同因数的位置存在一起,就可以logn查找是否存在了。
然后取存在的最大因数即可。
17.D. Present
题意:
给你n个数,求每对相加后的异或和。
思路:
很容易只要我们只要判断第i位数上有多少对的和在第i位为1,若为奇数则ans在第i位为1,否则为0。
所以我们就要判断怎么算这位上有多少个1。
假设我们想知道答案中第k个(在0索引中)位的值。然后我们可以注意到,我们只对第0到第k的位感兴趣,这意味着我们可以取2k+1为模的所有数字,此后,两个数字的总和不超过2k+2-2。
当且仅当和属于[2k;2k+1)或[2k+1+2k,2k+2-2]时,第k位为1
因此,我们必须计算给出属于这些段的和的数字对的数量。让我们对所有对数字进行排序(取模),并使用两个指针进行传递,或者对每个数字进行二进制搜索。
18.Unusual Competitions
题意:给你一个包含 ( 和 ) 的字符串。问你经过多少次交换,可以使字符串只包含(),(()),((())))组成的字符串。
思路:
19.蚯蚓
题意:
每次找到最长的蚯蚓,切成:
⌊
p
x
⌋
⌊px⌋
⌊px⌋ 和
x
−
⌊
p
x
⌋
x−⌊px⌋
x−⌊px⌋,求每次切的长度和最糊操作后的sum。
思路:
四、math:
1.I. Sad powers
题意:给出n个l和r,求出每个给出的[l,r]之间的可以使是另外一个数的k次方的数。(k>=2)
做法1:
容斥定理:
ci=pow(n,(1/i))可以得到所有以i为幂,小于等于n的底数的个数
那么最终答案是否是c1+c2+c3+…+ck呢
不是,因为有重复(例如:c2,c3间x^6这个数是被重复计算过的,但我们发现c2,c3的重复的这些数正好是c6(刚好满足容斥定理)
下面是 容斥表
对象是幂
奇数个数相乘的时候是加,n除以偶数个数相乘的时候是减。所以是
ans-=a[i](容斥表)
不过该做法在这道题会tle。
反省:
set lower_bound()返回的是第一个大于等于x的迭代器;
如要找到位置,还是用vector
lower_bound 返回第一个大于等于x的数的地址
upper_bound 返回第一个大于x的数的地址
//将vec变set
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(), vec.end()),vec.end());
2. D 重排列
题意:
一个序列的重排列是指对这个序列中的元素进行若干次(包括0次)交换操作后得到的新序列
在本题中,序列中可能出现重复的数字,他们被视作不同的元素
例如,序列1 1的重排列有两种
现在有两个长度为 N 的非负整数序列 A 和 B,问有多少种 A 的重排列满足对于所有的 1≤i≤N,有Ai≤Bi
由于答案可能很大,你只需要输出答案对1e9+7取模的结果
题解:
容易知道按升序将A和B排序不影响结果。
按标号从小到大考虑A的每个位置填什么数。
例:A(1,2,3);B(1,3,4)
则考虑第一个位置时,只能填1。
考虑第二个位置时,可以填2或3。
但是由于2和3在这里是完全等价的,也就是说我们并不关心填了谁。
那么我们只需要记录每一步有多少个数可填就好了,这个答案与之前填入的方案无关。当然这个要排除掉前面填的。比如全排列一下876;
所以计算个数直接从i开始就好了。
3.立方数
题意:对于给定的正整数 N,求最大的正整数 A,使得存在正整数 B,满足
A
3
B
=
N
A^3B=N
A3B=N
输入包含 T 组数据,
1
≤
T
≤
10
,
000
;
1
≤
N
≤
1
0
18
1≤T≤10,000;1≤N≤10^{18}
1≤T≤10,000;1≤N≤1018
思路:
首先暴力的想,做这道题直接质因数分解,复杂度
O
(
T
N
1
/
2
)
O(TN^{1/2})
O(TN1/2)
我们先做简单一点的优化,容易发现其实只要枚举
1
0
6
(
N
1
/
3
10^{6}(N^{1/3}
106(N1/3以内)4的质数就好,复杂度
O
(
T
N
1
/
3
/
l
n
(
N
1
/
3
)
)
O(TN^{1/3}/ln(N^{1/3}))
O(TN1/3/ln(N1/3))
再作进一步的分析,如果我们仅使用
N
1
/
4
N^{1/4}
N1/4(记为W)以内的质数去试除,那么最后余下的数X仅具有大于W的因子
此时X要么是一个完全立方数,要么对答案没有任何贡献, 只需要使用二分法来验证X是不是一个完全立方数即可
复杂度
O
(
T
N
1
/
4
/
l
n
(
N
1
/
4
)
)
O(TN^{1/4}/ln(N^{1/4}))
O(TN1/4/ln(N1/4)).
做法值得学习。
4.云
题意:
思路:
直接考虑问题较难,因为两种云都在运动。
但是我们可以考虑相对运动,将这个过程等效为左下角的云朝右上方移动。
在这个背景下我们容易发现:将所有的云投影成y=-x这条直线上的一条线段,则两朵云会相遇当且仅当他们的投影有交点。
这是一个简单的扫描线问题,将线段拆成端点后排序统计即可。
代码
收获:
1.投影的方式,将点投影到y=-x上可以直接用
p
y
−
p
x
p_y-p_x
py−px
2.求图中多少张不同种类到线段相交,可用类似区间左端点+1,右端点减1的方法。
A[i]排好序后
for(int i=1;i<=k;i++){
cnt[A[i].type]+=A[i].lr; //lr为左端点为1,右端点为-1,type为A种类为0,B种类为1
if(A[i].lr==1)
ans+=cnt[A[i].type^1]; //遇到B种类的左端点,直接加上覆盖的A种类的数量。
}
4.D. Count the Arrays
题意:
给你m个数,让其在一个长度为n的数组中排列,但是要保证先是单调递增,然后是单调递减,并且有两个元素是一样的。
思路:
首先我们要从m中选出n-1个元素排列
C
(
n
,
m
)
C(n, m)
C(n,m),因为有两个元素一样,所以要n-1,在这些元素里面我们要选择一个元素是一样的,这个时候可能会有n-1种,但是如果选择了这些元素中的最大值,那么就无法满足单调递增,所以选择一样的元素只有n-2种 ,然后就是选择位置了,位置的话,我们可以把最大值看成固定的,这样的话我们可以发现出了那两个一样的元素之外,其他的元素既可以放在左边也可以放在右边,那么就有n-3个这样的元素,所以排列的方式就有
2
n
−
3
2^{n-3}
2n−3,然后全部乘起来,答案就出来了。
五、Binary
1.Camp day1 乘法
反思:这类类似数学的题,又是第几大的多数情况考虑二分。
2.J. Code For 1
题意:给一个数n,和一个区间[l,r] (r-l<1e5,n<2^50),每次需要把序列中大于1的数字分成(n/2,n%2,n/2)(其中n/2是向下取整),直到所有数变成0或1,问[l,r]区间内有多少个1。
思路:
类似线段树的二分搜索。
1.
void dfs(ll a,ll b,ll l,ll r,ll n){
if(b<a||l>r) return;
ll mid=(a+b)>>1;
if(l>mid) dfs(mid+1,b,l,r,n/2);///全在右边
else if(r<mid) dfs(a,mid-1,l,r,n/2);//全在左边
else{ //在中间
ans+=n%2; //是奇数中间一定是1
dfs(a,mid-1,l,r,n/2);
dfs(mid+1,b,l,r,n/2);
}
}
2.
ll solve(ll n, ll left, ll right) {
if (left > R || right < L) return 0;
if (n < 2) {
if (left >= L && right <= R) return n;
else return 0;
}
ll mid = (left + right) >> 1;
return solve(n / 2, left, mid - 1) + solve(n % 2, mid, mid) + solve(n / 2, mid + 1, right);
}
2.C 汉诺塔
题意:现在你有 N 块 Xi*Yi的模板,用这些木板来玩汉诺塔。
第 i 块木板能放在第 j 块木板上方当且仅当 Xi<Xj 且 Yi<Yj,于是你很可能没法把所有的木板按照一定的次序叠放起来。
你想把这些木板分为尽可能少的组,使得每组内的木板都能按照一定的次序叠放。
你需要给出任意一种合理的分组方案。
思路:
先按x从小到大排序。
建立一个Stack,栈内按y从大到小排序。
当我们遍历x(从小到大),将y放入栈中(按顺序替换掉原来第一个y比它小的)的时候,最后的栈一定是所需最多的组数。
当能替换的时候,说明它们是可以同一组的(后面的y和x都会比被代替的y和x大)。不可以的话会继续补在栈中。
代码
六、模拟
1.I. Cloud of Hashtags
题意:n个字符串,要求不改变位置,删除最少的字符串的后缀,使这些字符串按照字典序非递减的顺序排列
七、单调队列
1.P1725 琪露诺
题意:小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只移动到区间[i+l,i+r]中的任意一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。
每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。
反思:
当dp超时时,应想想能不能去优化。如果是dp是取规定区间当最大最小值,则可以用单调队列优化。
八、概率
1.托米的字符串
反思:
一般这种全区间的解,有几种做法。单独元素算贡献,枚举某个点结尾或开始的总贡献,枚举区间长度贡献。
很重要
九、博奕
尼姆博弈
1.Camp day2 纳新一百的石子游戏
题目大意:
尼姆博弈,对于每个前缀的所有石子堆,询问先手必胜方案数。
反思:
1.关于尼姆博奕的获胜,一定是转化为异或和为0的状态。
2.异或不同为1,相同为0.
3.若k异或sum后满足大于k,则k在sum最高位的时候是1则满足,否则不满足。
十、DFS:
1.D. Garland
题意:这道题,根据他输入的顺序,每个数都会有一个编号,每行输入,(输入一棵树)第一个数表示的是他所指向的灯泡编号,第二个便是他自己的亮度值。此题就是要剪两刀,剪成三段一样亮度值的东西。
思路:
dfs。
收获:
可以用下面的方式获得每个子树的权值。
void dfs(int x){
sum[x]=val[x];
for(int i=0;i<path[x].size();i++){
if(vis[path[x][i]]==0){
dfs(path[x][i]);
sum[x]+=sum[path[x][i]];
}
}
}
十一、欧拉函数
1.GCD
题意:给定N,M求gcd(i,N)>=M的i的个数
思路:
我们设N=a*b,i=a*d
则gcd(a*d,a*b)=a*gcd(d,b);
i<=N,所以的d<=b(且d和b互质)
所以题目变成求φ(b)-1
b可由N得到。
但是每个a枚举b,铁定超时。
这里有一个操作,叫折半枚举。
其实也就是在算a*b==n的时候,同时算a和b,所以我们就可以只需要枚举sqrt(n)种情况,然后和它对应的情况就是 n/i
2.Longge’s problem
题意: Given an integer N(1 < N <
2
31
2^{31}
231),you are to calculate ∑gcd(i, N) 1<=i <=N.
思路:
gcd(i,n)是积性函数=>Σgcd(i,n)是积性函数。
设
f
(
n
)
=
Σ
g
c
d
(
i
,
n
)
f(n)=Σgcd(i,n)
f(n)=Σgcd(i,n), 且
n
=
p
1
a
1
∗
p
2
a
2
∗
p
3
a
3
∗
.
.
.
∗
p
k
a
k
n=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}*...*p_k^{a_k}
n=p1a1∗p2a2∗p3a3∗...∗pkak
所以求
f
(
n
)
=
f
(
p
1
a
1
)
∗
f
(
p
2
a
2
)
∗
f
(
p
3
a
3
)
∗
.
.
.
∗
f
(
p
k
a
k
)
f(n)=f(p_1^{a_1})*f(p_2^{a_2})*f(p_3^{a_3})*...*f(p_k^{a_k})
f(n)=f(p1a1)∗f(p2a2)∗f(p3a3)∗...∗f(pkak)
即求所有的
f
(
p
j
a
j
)
f(p_j^{a_j})
f(pjaj)
如果p是n的约数,那么满足
g
c
d
(
i
,
n
)
=
=
p
gcd(i,n)==p
gcd(i,n)==p的i的个数是
Φ
(
n
p
)
Φ(\frac{n}{p})
Φ(pn)(易证)
f
(
p
j
a
j
)
f(p_j^{a_j})
f(pjaj)
=
Σ
g
c
d
(
i
,
p
j
a
j
)
=Σgcd(i,p_j^{a_j})
=Σgcd(i,pjaj)
=
(
g
c
d
(
i
,
p
j
a
j
)
=
=
1
)
+
(
g
c
d
(
i
,
p
j
a
j
)
=
=
p
j
1
)
+
.
.
.
+
(
g
c
d
(
i
,
p
j
a
j
)
=
=
p
j
a
j
)
=(gcd(i,p_j^{a_j})==1)+(gcd(i,p_j^{a_j})==p_j^{1})+...+(gcd(i,p_j^{a_j})==p_j^{a_j})
=(gcd(i,pjaj)==1)+(gcd(i,pjaj)==pj1)+...+(gcd(i,pjaj)==pjaj)
=
Φ
(
p
j
a
j
)
+
p
j
∗
Φ
(
p
j
a
j
−
1
)
+
p
j
2
∗
Φ
(
p
j
a
j
−
2
)
+
.
.
.
+
p
j
a
j
−
1
∗
Φ
(
p
j
)
+
p
j
a
j
∗
Φ
(
1
)
= Φ(p_j^{a_j})+p_j*Φ(p_j^{a_j-1})+p_j^{2}*Φ(p_j^{a_j-2})+...+p_j^{a_j-1}* Φ(p_j)+p_j^{a_j}*Φ(1)
=Φ(pjaj)+pj∗Φ(pjaj−1)+pj2∗Φ(pjaj−2)+...+pjaj−1∗Φ(pj)+pjaj∗Φ(1)
因为
f
(
p
j
a
j
)
f(p_j^{a_j})
f(pjaj)中只有一个约数
p
j
p_j
pj,所以:
Φ
(
p
j
a
j
)
=
p
j
a
j
−
p
j
a
j
−
1
Φ(p_j^{a_j})=p_j^{a_j}-p_j^{a_j-1}
Φ(pjaj)=pjaj−pjaj−1.
整理得:
f
(
p
j
a
j
)
=
p
j
a
j
∗
(
1
+
a
j
∗
(
1
−
1
p
j
)
)
f(p_j^{a_j})=p_j^{a_j}*(1+a_j*(1-\frac{1}{p_j}))
f(pjaj)=pjaj∗(1+aj∗(1−pj1))
所以,
f
(
n
)
=
n
∗
(
1
+
a
1
∗
(
1
−
1
p
1
)
)
∗
(
1
+
a
2
∗
(
1
−
1
p
2
)
)
∗
.
…
…
=
n
∗
∏
a
i
∗
p
i
+
p
i
−
a
i
p
i
f(n)=n*(1+a_1*(1-\frac{1}{p_1}))*(1+a_2*(1-\frac{1}{p2}))*.…… =n* \prod\frac{a_i*p_i+p_i-a_i}{p_i}
f(n)=n∗(1+a1∗(1−p11))∗(1+a2∗(1−p21))∗.……=n∗∏piai∗pi+pi−ai;
ans=n;
sqr=floor(sqrt(1.0*n));
for(int i=2;i<=sqr;i++){
if(n%i==0){
a=0;p=i;
while(n%p==0){
a++;n/=p;
}
ans=ans+ans*a*(p-1)/p;
}
}
if(n!=1) ans=ans+ans*(n-1)/n;
还有比较暴力的做法:
我们枚举gcd(i,n)的所有情况即n的所有因子都有可能是他和其他数的最大公因数。我们假设M是n与i的最大公因数,那么所有与i互质且小于i的数与M的乘积我们设这个数为j,与n的最大公因数都为M,即gcd(j,n)=M,.这里所有与i互质且小于i的数也就是i的欧拉函数φ(i)而i=n/M。
但是我们直接1-n去枚举n的所有因子设为M,来枚举最大公约数的所有情况答案需要n的复杂度,在2^31次方的情况下是会超时的,所以我们采用折半枚举。
这个比较简单。但是可以时间比正解慢。
for(LL i=1;i*i<=n;i++){
if(n%i==0){
ans+=euler(n/i)*i;
if(i*i<n) ans+=euler(i)*(n/i);
}
}
3.Calculation 2
题意:小于N并且不互质的数字和
思路:所有小于n且与n为非互质数和=所有小于n数的和-所有小于n且与n互质的数的和
原理:
求所有小于N且与N为互质数的和:
1.欧拉函数可求与N互质的数的个数
2.
if gcd(n,i)=1 then gcd(n,n-i)=1 (1<=i<=n)
若已知m与n互质,则n-m也与n互质
那么,对于任何一个i与n互质,必然n-i也和n互质,所以PHI(N)必然是偶数(除了2)
所以从1到N与N互质的数的和为PHI(N)*N/2(一对一对算)
4.D. Same GCDs
题意:Calculate the number of integers 𝑥 such that 0≤𝑥<𝑚 and gcd(𝑎,𝑚)=gcd(𝑎+𝑥,𝑚).
思路:
十二、线段树
1.J 求函数
题意:
思路:
这是单点修改,所以不用lazy标记。
然后线段树首要的就是推两个区间合并的push——
我的 代码
十三、dfs序(两个时间戳)
1.A - Counting Offspring
题意:
给你一个树,问你对于每个节点,它的子树上标号比它小的点有多少个
思路:
dfs序,然后树状树状维护。
将i点按dfs序加入树状数组,然后查询i的子树(r[i],l[i]);
2.B - Apple Tree
题意:
卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果。卡卡很喜欢苹果。树上有N个节点,卡卡给他们编号1到N,根的编号永远是1.每个节点上最多结一个苹果。卡卡想要了解某一个子树上一共结了多少苹果。
现在的问题是不断会有新的苹果长出来,卡卡也随时可能摘掉一个苹果吃掉。你能帮助卡卡吗?
思路:
dfs序加线段树。
注意:
vectoredge[maxn];会超时。
用vector<vector >edge(maxn);
update(l[i]);线段树修改的是入时间戳,查询的是出时间戳到入时间戳。
3.E. Danil and a Part-time Job
题意:
一个操作,将子树的所有灯相反开关。
一个查询,子树的所有灯开关情况。
思路:dfs序加线段树
注意:除了上面一道题要注意的,还有一个,就是在建树的时候。
void dfs(int u,int fa){
l[u]=++cnt;
loc[cnt]=u; //重点
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i];
if(v==fa) continue;
dfs(v,u);
}
r[u]=cnt;
}
void build(int x,int L,int R){
tree[x].l=L;
tree[x].r=R;
if(L==R){
tree[x].sum=a[loc[L]]; //重点,反映射
return;
}
int mid=(L+R)>>1;
build(x<<1,L,mid);
build(x<<1|1,mid+1,R);
push_up(x);
}
十四、计算几何
1.Scrambled Polygon
逆时针排序。
十五、拓扑排序
字典序拓扑排序:用优先队列。在Q中放进入度为0的点,每次输出编号编号最小的结点,然后把它的后续结点的入度减1,入度减为0的再放入Q,这样就能输出一个字典序最小的拓扑序。
1.G. Swapping Places
题意:
给你一个列表,L个可以交换的对(相邻的才能交换),问最后列表的字典序最小
思路:
因为涉及到字典序,首先想到优先队列的拓扑排序。
其次就是怎么建图了。
拓扑排序的主要建图就是严格要求a在b前面的时候建一条从a到b的边。所以这里我们也要有这种思想。
所以
1.如果可以交换的话,这两个是不必建边的。
2.如果不能交换,那么这两个位置就一定要建边。(因为前面的一定会比后面的先出现)
所以对于每个
a
i
a_i
ai我们都要遍历一下所有种类,判断前面有没有出现过,且能不能交换,然后建边。
十六、欧拉回路
求欧拉回路可以用dfs,也可以不用。但是要手写栈模拟递归。
1.Code
题意:
有个保险箱子是n位数字编码,当正确输入最后一位编码后就会打开(即输入任意多的数字只有最后n位数字有效)……要选择一个好的数字序列,最多只需按键10n+n-1次就可以打开保险箱子,即要找到一个数字序列包含所有的n位数一次且仅一次。序列要为字典序。
思路:
欧拉回路,前n-1个与后面0~9为边,很明显是经过所有边回到原点。(每条边都必须经过有且仅有一次)。
十七、floyd
D. Roads in Berland
题意:有n个城市,现给出这n个城市之间没两个城市的距离,改变一些城市的距离,问最后所有这些路径长度最小之和。
思路:
因为数据很小,所以可以用floyd。
且每次增加一条路对floyd的影响是
//新增边为a到b权为c
if(dis[i][a]+dis[b][j]+c<dis[i][j]){
dis[i][j]=dis[i][a]+dis[b][j]+c;
dis[j][i]=dis[i][a]+dis[b][j]+c;
}
十八、树状数组
1.H 坐火车
题意:
思路:
反省:
对于权值树状数组的n一定要为权值的最大值maxn,而不是数量n。
找第一个小于k的,直接lower_bound()然后it–就是答案了。
2.F. Moving Points
题意:
给你n个点的x坐标和自己的速度v,问求每对点最近距离的和。
思路:
很容易推出
当
x
2
<
x
1
时
,
若
v
1
>
=
v
2
,
则
a
n
s
+
(
x
1
−
x
2
)
,
若
v
2
>
v
1
,
则
贡
献
为
0.
当x_2<x_1时,若v_1>=v_2,则ans+(x_1-x_2),若v_2>v_1,则贡献为0.
当x2<x1时,若v1>=v2,则ans+(x1−x2),若v2>v1,则贡献为0.
所以我们这里只需按x排序,然后依次加入点,并且判断前面有多少个v比它小的,则这些点会产生贡献。
我们可以用树状数组求:
先找个左边界,树状数组维护该点到左边届的和。同时权值树状数组维护v。
每次加入点,就判断有多少个v比它小的。
ans+=(query(x,cnt)*(a[i].x-l)-query(x,sum));
3.D. Babaei and Birthday Cake
题意:题意:按顺序给你n个圆柱体的底面圆半径,和高,当且仅当后面的圆的体积大于前面的圆的体积,就可以把他们合并成一堆,求这n个圆柱体可以组成的最大圆柱体的体积。
思路:由题意很容易想到是与树状数组或者线段树有关的题目(又有点类似LIS);
因为是后面加的要比前面大才能接上,所以这就很符合权值线段树了。
然后每个存入线段树的状态可能是当放入这个蛋糕时,能与前面比它小的组合的最大蛋糕。这里就能想到是dp的状态
dp[i]=max(dp[1…i-1])+a[i]
这里的权值需要离散化,离散化是可以离散浮点数的。
十九、STL
1.匹配星星
题意:
收获:
当遇到二维,要求按x排序,然后找到第一个小于y的点。可以:
struct node{
int a,b,c;
bool operator < (const node &x) const{
return a<x.a;
}
}mp[maxn];
sort(mp,mp+n);
multiset<int>que;
multiset<int>::iterator it;
it=que.lower_bound(mp[i].b); //找到第一个大于等于mp[i].b
if(it!=que.begin()){ //找到
que.erase(it);
}
que.insert(mp[i].b);
multiset的power_bound只知道地址,不知道是第几个元素
lower_bound(ms.begin(),ms.end(),a) 复杂度O(n)
ms.lower_bound(a) 复杂度O(logn)
2.操作序列
题意:
思路:
可以用map。
auto iter=map.begin()会按key排序,找到最小的key,刚好符合题目最小的位置。
if(map.lower_bound(pos-30)==map.lower_bound(pos+31)){
说明中间没有pos-30<key<pos+30
}
map.count(pos)可以算出key为pos的val。
代码
二十、LCA
[1.E - 1-Trees and Queries](E. 1-Trees and Queries)
题意:
给你一棵树,每条边可以走无数次。
询问:
在x和y间加一条边,求a到b的路程可能刚好为k吗?
思路:
树上两点的唯一路径是LCA_len(a,b)(模版)
因为可以来回走
所以在没新增边时a到b的路程为LCA_len(a,b)+2*Z。
新加边后为LCA_len(a,x)+LCA_len(y,b)+1+2*Z或LCA_len(a,y)+LCA_len(x,b)+1+2*Z。
2.E.Tree Queries
题意:
给你一棵树,之后m个查询。每次查询给你k个点,问是否有一个叶子到根的路径使得k个点在路径上或者和路径的距离。
二十一、Tarjan
1. B 图
题意:现在有一个N个点的有向图,每个点仅有一条出边
你需要求出图中最长的简单路径包含点的数量(1≤N≤1,000,000)
思路:记忆化搜索,tarjan模版题
二十二、Kuskal
1.I 导航系统
题意:
小 Q 所在的国家有 N 个城市,城市间由 N-1 条双向道路连接,任意一对城市都是互通的。
每条道路有一个长度,自然,小 Q 的导航系统能显示每对城市间的最短距离。
给定每对城市间的最短距离,你要判断距离表是否一定有误。
如果这张距离表是自洽的,那么请你按升序依次给出每条道路的长度。
对于全部的数据,1≤N≤500
思路:
显然数据给出的原图是一棵树。
容易发现,如果将输入的N(N-1)个距离看做N(N-1)条无向边的话,那么如果数据合法,原树就是这张新图的最小生成树。
证明:由于边权是非负的,可以考虑Kruskal算法的过程,每一次引入的边都是尽可能短的,所以一定是树中的边,通过简单的归纳即证。
所以求出最小生成树之后再进行验证就好了
收获:
判断无向图两个点是否已经联通
int fa(int v){ //类似并查集的操作
if(f[v]==v) return v;
else return f[v]=fa(f[v]);
}
for(int i=1;i<=n;i++) f[i]=i;
int tmp1=fa(a);
int tmp2=fa(b);
if(tmp1!=tmp2){ //判断是否相通了
f[tmp2]=tmp1;
}
二十三、最短路
1.D. Cow and Fields
题意:
给你一个有环无向图,和一个特殊点集,问如何选择点集中任意两个点并增加一条边,使得从1到n到最短路最大。
思路:
跑两边bfs,分别从1和n出发。
可以得到从1点到x点的最短距离和从n到x点的最短路。
我们要求的就是
m
a
x
(
m
i
n
(
x
a
+
y
b
,
x
b
+
y
a
)
)
max(min(x_a+y_b,x_b+y_a))
max(min(xa+yb,xb+ya))
其实x,y一定是最接近的两个相邻的特殊点。
所以跑一遍特殊点求i与i+1的最大值即可。
还有一种做法
m
i
n
(
x
a
+
y
b
,
x
b
,
y
a
)
min(x_a+y_b,x_b,y_a)
min(xa+yb,xb,ya),
假设:𝑥𝑎+𝑦𝑏≤𝑦𝑎+𝑥𝑏
则有:𝑥𝑎−𝑦𝑎≤𝑥𝑏−𝑦𝑏.
所以可以按xi-yi从小到大排序。这就说明了后面y的比前面x满足𝑥𝑎+𝑦𝑏≤𝑦𝑎+𝑥𝑏
所以
m
i
n
(
x
a
+
y
b
,
x
b
,
y
a
)
min(x_a+y_b,x_b,y_a)
min(xa+yb,xb,ya),等于
y
a
+
m
a
x
(
x
b
)
(
y
>
x
)
y_a+max(x_b)(y>x)
ya+max(xb)(y>x)
所以遍历上面的从小到大顺序,
每个
y
a
y_a
ya都与之前的
m
a
x
(
x
b
)
max(x_b)
max(xb)求一次最大。
二十四、树上启发式合并
1.A - Lomsat gelral
题意:
给你一颗树,每个结点都有一种颜色,为每个结点的子树包括了的最大颜色的数量,如果有多种颜色最大,就算它们的总和。
思路:
树上启发式合并
启发式合并。先通过一次dfs进行树链剖分,把重儿子和轻儿子区分开,然后再进行一次dfs统计答案。统计答案时先统计轻儿子的贡献,然后清空cnt数组,然后统计重儿子贡献,这时不清空cnt数组,返回到他们的父节点。此时,父节点就不需要再统计重儿子的贡献,直接统计轻儿子的贡献即可,这也就是dsu on tree的意义。
代码
2.Tree Requests
题意:给你一颗树,每个结点都有一个英文字母,多个询问,问你在一个子树x在总深度为d的结点上的字母能不能组成一个回文串。
思路:很容易想到其实要把所有的结点的每个深度答案都求出来。这就是用树上启发式合并。
先求轻儿子,然后清空,然后求重儿子和轻儿子。
到那个结点到时候算一下那个结点所需要到深度到答案。
代码
3.阔力梯的树
题意:给你一颗树,让你求每个结点的子树的权值排序后的
。
思路:
因为每个结点都要求,所以一定要用树上启发式合并。
然后具体优化是:
set来存子树的权值。然后最关键的优化就是取一个值的时候怎么快速的去算对权值的影响,因为不可能每个结点都遍历一次set,所以就得用二分找到放入的位置,然后消去加入后的影响。set::iterator it=se.lower_bound(num)
二十五、计算几何
1.牛牛与星辰
题意:
给你n个点,问有多少个三角形(三个点)组成的面积在[L,R]。
思路:
代码
二十六、树上差分
点差分:
设将两点𝑢,𝑣之间路径上的所有点权增加𝑥,𝑜=𝐿𝐶𝐴(𝑢,𝑣),𝑜的父亲节点为𝑝,则操作如下:
d
i
f
f
[
u
]
+
=
x
,
d
i
f
f
[
v
]
+
=
x
,
d
i
f
f
[
o
]
−
=
x
,
d
i
f
f
[
p
]
−
=
x
;
diff[u]+=x,diff[v]+=x,diff[o]-=x,diff[p]-=x;
diff[u]+=x,diff[v]+=x,diff[o]−=x,diff[p]−=x;
这样,只要𝑑𝑓𝑠一遍,遍历时统计以每个节点为根的树的节点的权值和,就是当前节点的最终权值!
边差分:
设将两点𝑢,𝑣之间路径上的所有边权增加𝑥,𝑜=𝐿𝐶𝐴(𝑢,𝑣),以每条边两端深度较大的节点存储该边的差分数组,则操作如下:
d
i
f
f
[
u
]
+
=
x
,
d
i
f
f
[
v
]
+
=
x
,
d
i
f
f
[
o
]
−
=
2
∗
x
;
diff[u]+=x,diff[v]+=x,diff[o]-=2*x;
diff[u]+=x,diff[v]+=x,diff[o]−=2∗x;
同样地,只要𝑑𝑓𝑠一遍,遍历时统计以每个节点为根的树的节点的权值和,就是当前节点到父亲节点的边的最终权值了!
1. Milk Visits
题意:
给出树状图,每个点上有对应的牛奶类型;
问给出的路中有没有对应的的牛奶;
思路:
树上差分。
用前缀和差分算。
从根结点到每个子结点求前缀和。然后
x,y,LCA(x,y),fa(LCA(x,y))
x到y上到路径的pre[x]+pre[y]-pre[LCA(x,y)]-pre[fa(LCA(x,y))]
二十七、树的直径方法
1.树上子链
题意:
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。
思路:
树上dp。看上面教程
二十八、并查集
1.D. Recommendations
题意:给定所有书籍的本数以及增加1本所需消费。求最小花费使得所有书籍数量不同
思路:贪心。所有书籍按照所需消费从大到小排序。越靠前增加的次数越少。并查集维护当前num至少要增加到多少。
这里的并查集很妙,用map维护fa数组(因为数太大)。
面对这种跳跃的,都可以用并查集来做。并查集可以优化跳跃的时间。
二十九、AC自动机
1.P3808 【模板】AC自动机(简单版)
题意:给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
思路:
AC自动机模版题
三十、交互题
D. Kuroni and the Celebration
题意:
给你一个以为止结点为根的树,让你在n/2次操作中找到这个根结点,你每次查询两个结点x,y会返回LCA(x,y)。
思路:
把1当成根,跑出叶子,查询两个叶子结点u,v,如果答案w不是u,v,就把包含u,v的子树删了,如果w是u,那根就是u,因为是叶子,只连了一条边
w为根带u,v的子树删了,每次最少删两个点,可以n/2次完成
实际就是先把1当根,跑到叶子结点,路径上到都标记,然后再跑一次1为根都其他叶子结点。
根到另一个根一定是唯一一条路径,如果1是我们要找到根,则得到的输入也一定就是1,否则就会是其他w,这时候w一定会比w到刚刚找到到两个叶子结点到路径上到没一个点更接近根,所以刚刚跑过到路径不用再访问。
这时候我们就要判断这个w是不是根。就进行和上面1一样的操作即可。