T1:首先用tarjan求出所有的点双,然后建圆方树。具体的建法是将当前点双的割点作为父亲向一个新建的方点连边,然后方点在想点双中其他点连边。
在建好圆方树之后我们就树形dp。设f[i]表示从i的子树到i的最长链的长度。对于每个x,如果x为圆点则像常规一样转移;若x为方点则要处理一下:把当前方点对应的点双中的点拉出来排成一列,然后复制一遍(处理环的情况)。接着我们枚举i、j(i<j),若j-i<=n/2则用f[i]+f[j]+j-i去更新ans。求f[x]类似。至此这道题就做完了。
T2:我们只讨论左小右大的情况,左大右小的直接将整个数列去相反数即可。
首先设L[x]表示从x往前第一个比a[x]大的位置+1,R[x]表示往右第一个比a[x]小的位置-1。
若一个区间[l,r]是合法的,那么必定存在R[l]>=r且L[r]<=l。反过来推也是对的。
现在我们枚举右端点r,每次r往后移一位时,对于那些R[x]<r的位置以后都不能再用了。而L[r]~r中那些可以用的位置的最大长度则会全部更新为右端点为r时的长度。这样我们就可以用一颗线段树来维护答案,线段树的每一个节点维护当前区间能用的最靠左的左端点,区间最大答案,以及区间修改标记。我们首先把R单调排序,每次右移r时先删去那些以后再也不能用的左端点,然后区间修改L[r]~r。每次查询的时候就查询[l,r]的最大答案即可。
T3:李超树模板题。