P3509 [POI2010]ZAB-Frog (尺取+倍增)

传送门

数轴上有n个点按点权大小升序排列,每个点上有一只青蛙,各自独立行动。每一步都会跳到距离他第k近的点(,距离等于点权之差的绝对值,如果存在两个这样的点,就跳到下标较小的那个), 问每只青蛙跳m步之后到达的位置。

(1<=n<=1e6,1<=m<=1e18)

首先我们通过尺取法预处理出每只青蛙跳一步到达的位置。

然后倍增即可。

to[i][j]=to[to[i][j-1]][j-1]

枚举m的二进制的每一位,每次跳2^i步。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    while(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k;
ll m;
ll p[maxn];
ll a[maxn][65];//i跳1步 
ll ans[maxn];
int main()
{
    
    
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++) scanf("%lld",p+i);
    a[1][0]=k+1;
    int l=1,r=k+1;//双指针 固定距离 k+1 其中k个是距离某个数前k小
    for(int i=2;i<=n;i++)
    {
    
    
        while(r+1<=n && p[i]-p[l] > p[r+1]-p[i]) l++,r++;
        if(p[i]-p[l] >= p[r]-p[i]) a[i][0]=l;
        else a[i][0]=r;
    }
	for(int i=1;i<=64;i++)
	{
    
    
		for(int j=1;j<=n;j++)
			a[j][i]=a[a[j][i-1]][i-1];
	}
    for(int i=1;i<=n;i++) ans[i]=i;
    for(int i=0;(1ll<<i)<=m;i++)//跳2^i步
    {
    
    
        if((m>>i)&1) //每只青蛙从ans[j]的位置出发 跳2^i步
        {
    
    
            for(int j=1;j<=n;j++) ans[j]=a[ans[j]][i];
        }
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

当然倍增可以用滚动数组优化空间,

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e6 + 10;
const ll mod = 1e9 + 7;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    while(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    while(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,k;
ll m;
ll p[maxn];
ll a[maxn];//i跳1步 其实就是维护连续区间内距离某个数前k小
//跳2^j步到达的位置 滚动数组
ll ans[maxn];
ll f[maxn];
int main()
{
    
    
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++) scanf("%lld",p+i);
    a[1]=k+1;
    int l=1,r=k+1;//双指针 固定距离 k+1 其中k个是距离某个数前k小
    for(int i=2;i<=n;i++)
    {
    
    
        while(r+1<=n && p[i]-p[l] > p[r+1]-p[i]) l++,r++;
        if(p[i]-p[l] >= p[r]-p[i]) a[i]=l;
        else a[i]=r;
    }
    for(int i=1;i<=n;i++) ans[i]=i;
    for(int i=0;(1ll<<i)<=m;i++)//跳2^i步
    {
    
    
        if((m>>i)&1) 
        {
    
    
            for(int j=1;j<=n;j++) ans[j]=a[ans[j]];
        }
        //a[i]是从i位置出发 跳2^x步 保存给f[i]
        for(int i=1;i<=n;i++) f[i]=a[i];
        //f[f[i]]就是从i出发 先跳2^x步,再跳2^x步 
        for(int i=1;i<=n;i++) a[i]=f[f[i]];
    }
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121296172