POJ 2886 线段树对约瑟夫问题的模拟

这个问题其实就是对约瑟夫问题的模拟,单纯的暴力模拟肯定是会超时的,用线段树来优化,只要算出当前要走的人在当前的数组中是第几个人就可以了

刚开始的时候没怎么弄明白,后来看了人家的分析之后才茅塞顿开

如果当前走的是第k个人,他报的数是m,那么下一个要走的人就是 k-1+m(当然还得取模)。为什么是这样呢,k走了之后,其实k之后的人相当于都向前挪了一个位置,所以需要减一个1. 在取完模以后,算得的结构就是下次要走的人是当前队伍中的第几个人,然后通过线段树去查找一下就好,线段树的节点记录其对应区间内的人数和,所以,查找时只需:

如果查找的第x个人不超过左子树的节点值,即左子树中有那么多的人,此时往左子树查找;否则,往右子树查找,但是要注意,必须将查找的人数减去左子树的人数

找到人之后应该对该节点(人最后肯定是一个节点)及其所有父节点的值更新,节点的值减1,然后push_up一下就好了,返回的值就是该人在最初队列中的坐标

下面还有一个问题,怎么使得他的约数个数最多呢??网上有很多解法都是反素数,在这里我用了筛法,把1-500000的每个数的约数个数给算出来了,具体怎么筛的通过代码很容易看出来,筛完之后,在ans数组里记录的是ans[i]表示有i个人时最多能得到的糖的数量,index[i]记录当有i个人时,第index[i]个跳出的孩子能得到最多的糖

以下就是代码:

#include <cstdio>
#include <cstring>
#define FF(i,a,b) for(int i=a; i<b; i++)
const int MAX = 500010;
int sum[MAX<<2];
template <class T>
T max(T a,T b)
{
    return (a>b)?a:b;
}
void push_up(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt] = 1;
        return;
    }
    int mid = r+l>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    push_up(rt);
}
int update(int p,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt] = 0;
        return l;
    }
    int mid = r+l>>1;
    int res;
    if(p<=sum[rt<<1]) res = update(p,l,mid,rt<<1);
    else res = update(p-sum[rt<<1],mid+1,r,rt<<1|1);
    push_up(rt);
    return res;
}
struct point
{
    char s[15];
    int x;
}ch[MAX];
int f[MAX];
int ans[MAX];
int index[MAX];
int main()
{
    int n,k;
    memset(f,0,sizeof(f));
    //下面的双重循环就是筛法求约数个数,f里存的是除1和本身外的约数个数
    for(int i=2; i*2<MAX; i++)
    for(int j=i*2; j<MAX; j+=i)
    f[j]++;
    ans[1] = 1;
    index[1] = 1;
    for(int i=2; i<MAX; i++)
    if(ans[i-1]>=f[i]+2)
    {
        ans[i] = ans[i-1];
        index[i] = index[i-1];
    }
    else
    {
        ans[i] = f[i]+2;
        index[i] = i;
    }
    while(scanf("%d%d",&n,&k)==2)
    {
        build(1,n,1);
        for(int i=1; i<=n; i++)
        scanf("%s%d",ch[i].s,&ch[i].x);
        int x = index[n];
        int tt;
        for(int i=0; i<x; i++)
        {
            tt = update(k,1,n,1);
            if(i==x-1) break;
            if(ch[tt].x>0) k = (k+ch[tt].x-2)%sum[1]+1; //计算下一次的跳出的人在现在这个队列中排第几
            //注意是从1开始记得,所以最后有个+1,括号里是-2,自己算一下就能体会
            else k = ((k+ch[tt].x-1)%sum[1]+sum[1])%sum[1]+1; //与上面原理一样
        }
        printf("%s %d\n",ch[tt].s,ans[n]);
    }
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,可以得知这是一道关于迷宫问题的题目,需要使用Java语言进行编写。具体来说,这道题目需要实现一个迷宫的搜索算法,找到从起点到终点的最短路径。可以使用广度优先搜索或者深度优先搜索算法来解决这个问题。 下面是一个使用广度优先搜索算法的Java代码示例: ```java import java.util.*; public class Main { static int[][] maze = new int[5][5]; // 迷宫地图 static int[][] dir = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; // 方向数组 static boolean[][] vis = new boolean[5][5]; // 标记数组 static int[][] pre = new int[5][5]; // 记录路径 public static void main(String[] args) { Scanner sc = new Scanner(System.in); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { maze[i][j] = sc.nextInt(); } } bfs(0, 0); Stack<Integer> stack = new Stack<>(); int x = 4, y = 4; while (x != 0 || y != 0) { stack.push(x * 5 + y); int t = pre[x][y]; x = t / 5; y = t % 5; } stack.push(0); while (!stack.empty()) { System.out.print(stack.pop() + " "); } } static void bfs(int x, int y) { Queue<Integer> qx = new LinkedList<>(); Queue<Integer> qy = new LinkedList<>(); qx.offer(x); qy.offer(y); vis[x][y] = true; while (!qx.isEmpty()) { int tx = qx.poll(); int ty = qy.poll(); if (tx == 4 && ty == 4) { return; } for (int i = 0; i < 4; i++) { int nx = tx + dir[i][0]; int ny = ty + dir[i][1]; if (nx >= 0 && nx < 5 && ny >= 0 && ny < 5 && maze[nx][ny] == 0 && !vis[nx][ny]) { vis[nx][ny] = true; pre[nx][ny] = tx * 5 + ty; qx.offer(nx); qy.offer(ny); } } } } } ``` 该代码使用了广度优先搜索算法,首先读入迷宫地图,然后从起点开始进行搜索,直到找到终点为止。在搜索的过程中,使用标记数组记录已经访问过的位置,使用路径数组记录路径。最后,使用栈来输出路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值