【线段树思想】SDUT 3475 最后の汤圆

本文介绍了一个有趣的问题:如何确定在一种特殊吃法下最后一个被吃掉的汤圆编号。通过构建线段树并使用特定的搜索和更新策略,解决了这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最后の汤圆
Time Limit: 1000MS Memory Limit: 65536KB
Problem Description

又到了汤圆星球一年一度的汤圆节了,恰好这一天是地球的元宵节,出于这个巧合,两球之间早在数百年前就建立起了长久稳固的友好外交关系。

良好的外交需要物质的支持,此次汤圆星球外交使者向地球朋友们进贡了上等的汤圆。汤圆星球的汤圆使者送来的n(1<=n <= 500000)个汤圆中,每个汤圆都有一个口感指数ti(0 < ti<=10^8),而且汤圆使者还为地球朋友们提供了一种新(qi) 颖(pa)的汤圆吃法,据说可以使口感倍增。

这种尽(sang)善(xin)尽(bing)美(kuang)的吃法是:先将n个汤圆排成一个圈儿,并且按照顺时针方向给汤圆编号为1~n, 然后从编号为k(1<=k<=n)的汤圆开始吃,每吃掉一个汤圆后,会获得它的口感指数,这个口感指数决定了下一个即将被吃掉的汤圆:

假设这个刚被吃掉的汤圆的口感指数是A,那么从刚被吃掉的这个汤圆开始,顺时针数第A个汤圆就是下一个即将被吃掉的汤圆。

现在按照汤圆编号1~n的顺序给定每个汤圆的口感指数ti,求最后一个被吃掉的汤圆的编号。

Input

多组输入,数据范围见题目描述。

对于每组输入第一行有两个整数n和k,分别代表汤圆的数量和第一个被吃的汤圆。

第二行有n个以空格间隔的整数t1 t2 t3 … tn,第i个整数ti代表第i个汤圆的口感指数。

Output

对于每组数据输出一个整数,表示最后一个被吃掉的汤圆的编号。

Example Input

4 2
1 3 5 7
4 3
2 3 3 3

Example Output

4
1

思路:

假期回来的比赛防AK的一道题,完全想不到这种思路,听东庆巨巨讲解了一波才懂得。我们需要把这n个汤圆的下标构成一个线段树。结构体成员定义一个pos,用来判断对应下标的位置在树的左边还是右边。pos是取右孩子的值。这样查找的时候就可以判断如果大于左孩子就去右孩子寻找下标值。如果要删除第k个汤圆(找第k个汤圆原来的下标),我们就将(原来的下标-n)的下标都减1,这样就出现了重复的下标,重复的下标中右边的是不需要的(已经被删除了)。我们需要查找下一个我们要删除下标(原来的下标),具体还是看代码吧。

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int pos, lazy;
};
node tree[4 * 500000];
int a[500055];
#define MID int mid = (l + r) / 2
#define lson root<<1
#define rson root<<1|1
node merge_(node x, node y)
{
    node t;
    t.lazy = 0;
    t.pos = y.pos;//等于右孩子的值
    return t;
}
void build(int root, int l, int r)//初始化以下标建线段树
{
    tree[root].lazy = 0;
    if(l == r)
    {
        tree[root].pos = r;
        return ;
    }
    MID;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    tree[root] = merge_(tree[lson], tree[rson]);
}
void pushdown(int root, int l, int r)//向下更新
{
    if(tree[root].lazy)
    {
        tree[lson].lazy += tree[root].lazy;
        tree[rson].lazy += tree[root].lazy;
        tree[lson].pos += tree[root].lazy;//pos是右孩子的值,也就是加上lazy的值就可以了
        tree[rson].pos += tree[root].lazy;
        tree[root].lazy = 0;
    }
}
void updata(int root, int l, int r,int ul, int ur, int v)
{
    if(ul <= l && r <= ur)//在区间范围内
    {
        tree[root].lazy += v;//lazy的值-1
        tree[root].pos += v;//pos的值-1
        return ;
    }
    pushdown(root, l, r);
    MID;
    if(mid >= ul) updata(lson, l, mid, ul, ur, v);
    if(mid < ur) updata(rson, mid + 1, r, ul, ur, v);
    tree[root] = merge_(tree[lson], tree[rson]);
}
int Search(int root, int l, int r, int po)//查找当前下标po,对应的原来下标是几
{
    if(l == r)
    {
        return l;
    }
    pushdown(root, l, r);
    MID;
    int red;
    if(po <= tree[lson].pos) red = Search(lson, l, mid, po);
    else red = Search(rson, mid + 1, r, po);
    return red;
}
int main()
{
    int n, k, v;
    while(~scanf("%d %d", &n, &k))
    {
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
        }
        build(1, 1, n);
        int t = n;
        while(t--)
        {
            v = Search(1, 1, n, k);//要删除第几个数对应的原来的下标
            if(!t)
            {
                printf("%d\n", v);
                break;
            }
            updata(1, 1, n, v, n, -1);
            k = (a[v] + k - 1 - 1) % t + 1;//要删除第几个数
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值