CODEFORCES 13E. Holes

文章描述了一种数学问题,涉及到球在具有特定能量值的洞中弹跳的情况。给定每个洞的能量值po[i],球会从一个洞弹跳到下一个洞。有两种操作:改变洞的能量值或查询球从特定洞弹出的洞号和次数。解决方案是通过分块动态规划和回溯方法,在O(sqrt(n)*m)的时间复杂度内处理问题。
摘要由CSDN通过智能技术生成

 

 题目大意:
 

给你n个编号1到N的洞(N<=1e5)。

每一个洞有个能量值po[i]。当你在i号洞里放一个球时.这个球会弹到i+po[i]号洞内。

然后弹到i+po[i]+po[i+po[i]]......也就是说球每到一个洞i就会弹到i+po[i]号洞内。如今有两种操作。

1.0 a b : 把a号洞的po改成b

2.1 a  :  询问当把球放到a号洞内时。它是从几号洞弹出界的。和它一共弹了几次。

分析:
首先,我们先考虑没有改动操作,那么不停的用i+a[i] 的操作可以找到大于N的位置;再用回溯的方式,我们可以很快就可以一一找到每个点最终的答案。

那么当考虑改动时,我们可以把序列分为sqrt(N)块,再不停用i+a[i]的操作找到i跳到后面若干块的位置,而修改时,我们最多只要对sqrt(N)个数据进行暴力就可以完成。

算法的复杂度O(sqrt(n)*m)

//code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 1000;
const int sq = 316;
int a[maxn];
int dp[5*maxn];
int vis[5*maxn];
int ti[5*maxn];
vector<int>ver[sq + 10];
void update(int x, int n){
    int yc = x;
    int r = ((x - 1)/sq)*sq + sq;
    int l = ((x - 1)/sq)*sq + 1;
    if(yc+a[yc] <= r&&yc + a[yc] <= n){
        dp[yc] = dp[yc + a[yc]];
        ti[yc] = ti[yc + a[yc]] + 1;
    }else  {
        dp[yc] = yc + a[yc];
        ti[yc] = 1;
    }
    fill(vis + l + 1, vis + yc + 1, 0);
    for(int i = yc; i >= l; i--){
        if(a[i] + i == yc){
            vis[i] = 1;
            dp[i] = dp[yc];
            ti[i] = ti[yc]+1;
        }else if(i+a[i] < yc && vis[i+a[i]] == 1){
            dp[i] = dp[yc];
            vis[i] = 1;
            ti[i] = ti[i+a[i]] + 1;
        }
    }

void query(int x, int n, int &ans1, int &ans2){
    while(dp[x] <= n){
        ans2 = ans2 + ti[x];
        x = dp[x];
//        cout<<"yes\n";
    }
    ans2 = ans2 + ti[x];
    while(x <= n){
        ans1 = x;
        x = x + a[x];
//        cout<<"yes\n";
    }
}
int main(){
    int n, m, op, x, y;
    cin>>n>>m;
//    fill(vis, vis+maxn, 0);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    stack<int>sta;
    for(int i = 1; i <= n; i++){
        int r = ((i - 1)/sq)*sq + sq;
        int yc = i;
        if(vis[yc])   continue;
        while(yc <= n&& yc <= r){
            sta.push(yc);
            yc = yc + a[yc];
        }
        int la = 0;
        while(!sta.empty()){
            dp[sta.top()] = yc;
            ti[sta.top()] = la + 1;
            la = ti[sta.top()];
            vis[sta.top()] = 1;
            sta.pop();
         }
    }
    while(m--){
        scanf("%d", &op);
//        for(int i = 1; i <= n; i++)  cout<<dp[i]<<" ";
//        cout<<endl;
        if(op == 0){
            scanf("%d%d", &x, &y);
            a[x] = y;
            update(x, n);
        }else{
            scanf("%d", &x);
            int ans2 = 0;  int ans1;
            query(x, n, ans1, ans2);
            printf("%d %d\n", ans1, ans2);
        } 
        
    }
    
    return 0;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值