sgu-254 Strange Random

題目大意:

整數1N(N<=2106) 按順時針形成一個圈寫在黑板上。你需要重複如下操作:
從當前整數移動到從當前整數按當前方向開始數的第Q(Q<=10) 個整數,然後擦掉這個整數並且跳到這個整數的下一個整數。如果下一個整數是奇數,那麼下一次操作方向應該是順時針,如果是偶數應該是逆時針。
一開始你在整數1 上,方向爲順時針,要你求最後留下的整數是哪個?

簡單來說就是擦數字問題的改版。

解題思路:

首先看到題目的思路就是用並查集搞,時間複雜度O(N*Q),但是發現要開兩個大小爲N 的數組,一個存順時針,另一個是逆時針,但是sgu 卡內存,所以果斷放棄。
後來看到翱犇博客裏說可以直接爆搜,然後寫了一發,結果是超時了(估計姿勢不對)
之後就膜拜了一下翱犇的代碼中那段註釋掉了的剪枝。
基本思想是這樣的:就是每暴力執行T 次操作後,就將整個環更新,把原來已經刪掉的數字去掉,組成一個新的沒有被刪過數字的環,然後繼續暴力。
然而我還是TLE 了,仔細觀察我發現問題在寫的是
now=now%n+1而正確的姿勢是now++,if(now==n+1) now=1因爲取模時間長。這個很坑。。。。。。

AC代碼:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>

using namespace std;

int n,q;
int num[2000010]={0};

int main()
{
    scanf("%d%d",&n,&q);
    int g=1,now=1,len=n/20,p=n,np;
    if(len==0) len=n;
    for(int i=1;i<=n;i++)
        num[i]=i;
    for(int i=1;i<n;++i)
    {
        for(int j=1;j<q;++j)
        {
            if(g==1)
            {
                for(;;)
                {
                    now++;
                    if(now==p+1) now=1;
                    if(num[now]!=-1)break;
                }
            }
            else
            {
                for(;;)
                {
                    now--;
                    if(now==0) now=p;
                    if(num[now]!=-1)break;
                }
            }
        }
        num[now]=-1;
        for(;;)
        {
            now++;
            if(now==p+1) now=1;
            if(num[now]!=-1)break;
        }
        if(num[now]&1) g=1;
        else g=-1;
        if(i%len==0)
        {
            np=0;
            for(int j=1;j<=p;++j)
                if(num[j]!=-1)
                {
                    num[++np]=num[j];
                    if(num[j]==num[now])now=np;
                }
            p=np;
        }
    }
    printf("%d\n",num[now]);
    return 0;   
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章