Hrbust 1390 Leyni, LOLI and Numbers【思维+树状数组+二分+双向链表模拟】好题!好题!好题!

142 篇文章 0 订阅
75 篇文章 0 订阅

Leyni, LOLI and Numbers
Time Limit: 2000 MSMemory Limit: 65536 K
Total Submit: 52(19 users)Total Accepted: 27(17 users)Rating: Special Judge: No
Description

Professor Leyni likes to play with LOLIs. This year, Leyni meets several LOLIs on the playground which is a circle and begins to play game with them again.

At the beginning, he makes N LOLIs around the playground and numbers them from 1 to N, so that the LOLI1 is next to the LOLI2, the LOLI2 is next to the LOLI3 ... the LOLIN is next to the LOLI1.

Then Leyni says N numbers A1, A2 ... AN and the LOLIs quit the game under the direction of him according to the numbers.

For the 1st round, he counts A1 times from the LOLI1 one by one. When reaching the last LOLI, he goes back to the first.

For each round, the LOLI which Leyni reaches will quit the game, next round, Leyni will begin from the next LOLI and count as same as the 1st round.(When reaching the last LOLI, he goes back to the first.)

For example there are 3 LOLIs and 3 numbers, the numbers are 2, 4 and 3

The 1st round, counting from LOLI1, after 2 times (LOLI12), Leyni reaches LOLI2, LOLI2 quits.

The 2nd round, counting from LOLI3, after 4 times (LOLI3131), Leyni reaches LOLI1, LOLI1 quits.

The 3rd round, counting from LOLI3, after 3 times (LOLI333), Leyni reaches LOLI3, LOLI3quits.

So the LOLIs quit according to the order of 2 1 3.

Please help Leyni to calculate the order that the LOLIs quit.

Input

There are multiple test cases. The first line of input is an integer T indicating the number of test cases. Then T test cases follow.

For each test case:

Line 1. A number N (1 ≤ N ≤ 105), representing the number of LOLIs.

Line 2. This line contains a sequence of N integers, indicating the numbers that Leyni says. All the numbers are in the range [1, 109].

Output

For each test case:

Line 1. Output the order that the LOLIs quit in a single line. Separate them by a single space.

Sample Input

2

3

2 4 3

5

2 5 3 1 3

Sample Output

2 1 3

2 3 1 4 5

Source
哈理工2012春季校赛 - 现场赛
Author
齐达拉图@HRBUST

题目大意:

一共有N个人,围成一个圈,1和N相邻。

而且一共有N个操作 ,每个操作表示从当前光标这个位子开始数人,数到第Ai个人使得这个人(ans)离开这个圈。然后光标变成了这个人后边的第一个人.

问每轮次出局人的编号。

不妨对照样例手写一下就能理解了啊。



思路:


1、首先明确,既然是一轮次出局一个人,那么在第i轮,就剩下了n-i+1个人,那么对于操作数Ai.我们可以加入取模操作,去掉多余的操作,使得操作数在一圈之内,能够找到出局的人的编号。


2、那么接下来我们思考,怎样优化找人这个操作呢?

很显然找人操作,就是在当前编号now这个人后边,找Ai个人咯。那么我们这里可以引入树状数组咯.

树状数组可以查询一个区间中剩余的人的个数咯。

那么一开始我们将1~N这些位子都置1咯.

然后每一次出局一个人就将这个位子置0咯.

所以每一次查询一个人身后第Ai-1个人的位子,就可以用树状数组来维护咯.

那么我们总不能从now+1开始枚举,直到枚举到这一轮次的结果位子去查询吧,那样的话时间复杂度可以视为O(n^2logn)了呀.

仔细想想这里包含一个单调性啊,所以我们可以二分这一轮次的结果位子啊。

所以时间复杂度降到了O(nlognlogn)了呀.

2000ms吃得消了啊、


3、然后按照上述思路一顿写,发现如果now开始数,如果数到了N都不够人数的话,又要从1开始数起啊,写两次二分很费劲啊。

那么我们不妨拓展出来N个人啊,i和n+i代表一个人啊。

那么如果此时有:

1 0 0 1 1还在场上,同时now==4.要数3个人的话,我们此时拓展出来N个人,那么就是:1001110011.那么二分三个人的结果位子就是6啊.1和6代表同一个人啊,所以结果就是1号啊.

这里时间复杂度变成O(nlog2nlog2n)了,然而2000ms还是吃得消啊;

所以这里就二分+树状数组来查询咯。


4、最后一个问题啊,我们如何确定当前位子的下一个人的位子呢?

可以维护一个链表啊,pre【i】表示这个人前边人的编号,nex【i】表示这个人后边人的编号。

过程模拟维护一下就能确定了啊。


5、这个题还卡了常啊,注意查询操作不要太多啊,会TLE的0...........0


Ac代码:


#include<stdio.h>
#include<string.h>
using namespace std;
int tree[200505];//树
int a[200505];
int nex[200505];
int pre[200505];
int output[200505];
int n;
int lowbit(int x)//lowbit
{
    return x&(-x);
}
int sum(int x)//求和求的是比当前数小的数字之和,至于这里如何实现,很简单:int sum=sum(a[i]);
{
    int sum=0;
    while(x>0)
    {
        sum+=tree[x];
        x-=lowbit(x);
    }
    return sum;
}
void add(int x,int c)//加数据。
{
    while(x<=n*2)
    {
        tree[x]+=c;
        x+=lowbit(x);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)tree[i]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i]%=(n-i+1);
            if(a[i]==0)a[i]=n-i+1;
            nex[i]=i+1,pre[i]=i-1;
            if(i==n)nex[i]=1;
            if(i==1)pre[i]=n;
        }
        int cnt=0;
        int now=1;
        for(int i=1;i<=n*2;i++)add(i,1);
        for(int i=1;i<=n;i++)
        {
            int ans=-1;
            int l=now;
            int r=n*2;
            while(r-l>=0)
            {
                int mid=(l+r)/2;
                int tmp=sum(mid)-sum(now-1);
                if(tmp<a[i])
                {
                    l=mid+1;
                }
                else
                {
                    if(tmp==a[i])ans=mid;
                    r=mid-1;
                }
            }
            if(ans<=n)
            {
                output[cnt++]=ans;
                add(ans,-1);
                add(ans+n,-1);
                nex[pre[ans]]=nex[ans];
                pre[ans[nex]]=pre[ans];
                now=nex[ans];
            }
            else
            {
                output[cnt++]=ans-n;
                add(ans,-1);
                add(ans-n,-1);
                nex[pre[ans-n]]=nex[ans-n];
                pre[ans[nex-n]]=pre[ans-n];
                now=nex[ans-n];
            }
        }
        for(int i=0;i<cnt;i++)
        {
            if(i!=0)printf(" ");
            printf("%d",output[i]);
        }
        printf("\n");
    }
}














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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值