原题链接:luogu
(蒯来的)
数轴上有n个点,有一个青蛙在这些点上跳;规则是每次向距当前点第k小的点跳,如果有相同距离则向下标较小的跳;求从每个点出发跳了m次后在哪里;
这个是我集训某天的原题。像这种第 k k k大的问题通常是最难处理的问题。但是我们非常明显的有一个暴力想法:由于 k k k是定值,预处理出每个点开始第 k k k大到哪,然后暴力跳一下即珂。 O ( n 2 + n m ) O(n^2+nm) O(n2+nm)???
珂是我真的就是一个智障 w o c woc woc,都想到这里了,再结合昨天讲倍增,不好好想想倍增做法! f ∗ ∗ k f**k f∗∗k!(我太菜了。。。)
倍增做法:设 j u m p [ i ] [ k ] jump[i][k] jump[i][k]表示从 i i i开始跳 2 k 2^k 2k步能到哪里,然后你就只要 j u m p [ i ] [ k ] = j u m p [ j u m p [ i ] [ k − 1 ] ] [ k − 1 ] jump[i][k]=jump[jump[i][k-1]][k-1] jump[i][k]=jump[jump[i][k−1]][k−1]转移,然后二进制拆分 m m m跳一下即珂。
但是我们还面临一个问题:求第 k k k大这件事,是 O ( n ) O(n) O(n)的( S T L STL STL函数 n t h _ e l e m e n t nth\_element nth_element珂以 O ( n ) O(n) O(n)做这个东西,而不是排序的 n l o g n nlogn nlogn)。一次 O ( n ) O(n) O(n),一共就 O ( n 2 ) O(n^2) O(n2),稳 T T T。
观察标签,我们猜这个东西用单调队列优化。其实不用单调队列,用双指针即珂,只不过用了单调队列的思想。设 l , r l,r l,r表示点 i i i到左边,右边的第 k k k大。初始值 l = 1 , r = k + 1 l=1,r=k+1 l=1,r=k+1。然后当我们的中心点 i i i增加时,左边的所有距离都增加了,所以 l l l会增加。右边的所有距离都减少了,同时, r r r也是会增加的。然后我们就不断的让 l , r l,r l,r增加,直到满足条件为止(注意 l < = i , r < = n l<=i,r<=n l<=i,r<=n)由于单调性, l , r l,r l,r虽然一次珂能动很多次,但是加起来肯定是 < = n <=n <=n的。这样就保证了时间复杂度。
代码:
#include
using namespace std;
namespace Flandle_Scarlet
{
#define N 1001000
#define ll long long
ll n,k,m;
ll p[N];
void Input()
{
scanf("%lld%lld%lld",&n,&k,&m);
for(int i=1;i<=n;++i)
{
scanf("%lld",&p[i]);
}
}
int jump[N][64];
void Soviet()
{
int l=1,r=k+1;
jump[1][0]=k+1;//处理出jump[1][0](非常明显就是k+1)
for(int i=2;i<=n;++i)
{
while(r<i) ++l,++r;
while(l<i and r<n)//说好的单调队列,实际上只要双指针模拟一下即珂
//然后队列。。。就神奇的没了。。。
{
if (p[r+1]-p[i]<p[i]-p[l])//不满足条件
{
++l,++r;
}
else break;
}
if (p[i]-p[l]>=p[r]-p[i]) jump[i][0]=l;//如果相同的时候也是取左边的,所以注意这里是严格的<=,不能换
else jump[i][0]=r;
}
for(int i=1;i<=62;++i)
{
for(int j=1;j<=n;++j)
{
jump[j][i]=jump[jump[j][i-1]][i-1];
//转移
}
}
for(int i=1;i<=n;++i)
{
int u=i;
for(int j=0;j<=62;++j)
{
if ((m>>j)&1)//数位拆分+倍增
{
u=jump[u][j];
}
}
printf("%d ",u);
}putchar('\n');
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
回到总题解界面