T2:
设f[i]表示当前第i个人认识的最右边的人的编号。
50分做法:
每次枚举l,r之间的i,当f[i]<r时ans=ans+r-f[i],然后把f[i]更新为r。
初始值:f[i]=i
100分做法:
我们发现f[i]是递增的,所以我们考虑用线段树维护f[i]。
设f1[]表示区间的最大值,f2[]表示区间的最小值,f3[]表示区间的和。
我们每次要找出一个s,使得l<=s<=r且f[s]<r且s最大,那么ans=(s-l+1)*r-sum(f[l~s]),这个式子由50分的式子得到。
求sum的一步可以用区间查询来完成,在做完这一步之后我们要把f[l~s]的值赋为r,这一步可以用区间修改来完成,关键是怎样求s。
我们可以用f1,f2来求s,我们每一次从线段树的根节点开始往下走,直到走到一个叶节点,这个叶节点就是答案,下面说说怎样走。
假设当前的左子树是l,右子树是r,要查找的值为w,那么
若f1[l]>=w,则往左走
else 若f2[r]>=w,则往左走
else 往右走
T3:设f[i][0/1]表示第i个位置是直接(0)还是间接(1)下载的。
那么f[i][0]=min(f[i+1][0],f[i+1][1])+c[i],f[i+1][1]=min(f[j][0]+(j-i+1)*(j-i)/2)。
这是O(n^2)的算法,只有60分,所以我们要优化。
我们发现,若当前有j<k且f[j][0]<=f[k][0],那么j一定比k优,所以我们可以用一个单调栈对f[i][0]进行维护。我们每一次都将f[i][0]入栈,把栈中<=f[i][0]的元素都弹掉,那么每次计算f[i][1]的时候只需枚举栈中的元素就可以了。这样可以得80分。
100分只需在这个基础上加一个小小的优化,每次枚举栈时要从上往下枚举,若当前的栈中元素的转移所需的费用((j-i+1)*(j-i)/2)已经不小于f[i][1]了,那么就break。这样就可以AC了。