2017.7.7 机房测试 (模拟,贪心,二分)

本文解析了三道NOIP模拟题,包括祖玛游戏过程回放实现、利用栈对数组进行排序以及序列生成问题。提供了详细的解题思路与代码实现。

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

hao

【问题描述】

祖玛是一款曾经风靡全球的游戏,其玩法是:在一条轨道上初始排列着若干个彩色珠子,其中任意三个相邻的珠子不会完全同色。此后,你可以发射珠子到轨道上并加入原有序列中。一旦有三个或更多同色的珠子变成相邻,它们就会立即消失。这类消除现象可能会连锁式发生,其间你将暂时不能发射珠子。
开发商最近准备为玩家写一个游戏过程的回放工具。他们已经在游戏内完成了过程记录的功能,而回放功能的实现则委托你来完成。
这里写图片描述
游戏过程的记录中,首先是轨道上初始的珠子序列,然后是玩家接下来所做的一系列操作。你的任务是,在各次操作之后及时计算出新的珠子序列。

【输入格式】

第一行是一个由大写字母’A’~’Z’组成的字符串,表示轨道上初始的珠子序列,不同的字母表示不同的颜色。
第二行是一个数字 ,表示整个回放过程共有 次操作。
接下来的 行依次对应于各次操作。每次操作由一个数字 和一个大写字母 描述,以空格分隔。其中, 为新珠子的颜色。若插入前共有 颗珠子,则 表示新珠子嵌入之后(尚未发生消除之前)在轨道上的位序。

【输出格式】

输出共 行,依次给出各次操作(及可能随即发生的消除现象)之后轨道上
NOIP 模拟题 hao
第 3 页 共 6 页
的珠子序列。
如果轨道上已没有珠子,则以“-”表示。

【样例输入】

ACCBA
5
1 B
0 A
2 B
4 C
0 A

【样例输出】

ABCCBA
AABCCBAA
ABBCCBA
-
A

【数据规模与约定】

的数据满足
1<=n<=1000
1<=m<=2000

【解题报告】

想不到第一道模拟题大部分人都没有过。。。。
因为它是一道恶心的模拟。
注意即使一开始出现了相邻的多余3个的球,也只有被同色碰撞才会消失。
一开始可能没有球(所以用gets)。
还有这个插入的写法简直就是黑魔法啊。。。
本着便于理解的原则,请点击http://www.2144.cn/flash/13326.htm

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 20005
char ch[N],temp[N],e;  
int n,size=0,pos;  

int res(int a)  
{  
    int head=a,last=a;  
    char elem;  
    elem=ch[a];  
    while(ch[head]==elem&&head) head--;
    if(head||ch[head]!=elem) head++;
    while(ch[last]==elem&&last<size) last++;  
    if(last-head>2)  
    {  
        strcpy(temp,ch+last);  
        strcpy(ch+head,temp);  
        size=size+head-last;  
        pos=head;  
        return 1;  
    }  
    else return 0;  
}  
int main()  
{   
    freopen("hao.in","r",stdin);
    freopen("hao.out","w",stdout);
    gets(ch);  
    while(ch[size]>='A'&&ch[size]<='Z')size++;  
    scanf("%d",&n);  
    while(n--)  
    {  
        scanf("%d %c",&pos,&e);  
        strcpy(temp, ch+pos);  
        strcpy(ch+pos+1,temp);  
        ch[pos]=e;size++;  
        while(res(pos)&&size);  
        if(size) puts(ch);  
        else puts("-");  
    }  
    return 0; 
}  

kun

【问题描述】

栈是一种强大的数据结构,它的一种特殊功能是对数组进行排序。例如,借助一个栈,依次将数组1,3,2按顺序入栈或出栈,可对其从大到小排序: 1入栈;3入栈;3出栈;2入栈;2出栈;1出栈。 在上面这个例子中,出栈序列是3,2,1,因此实现了对数组的排序。 遗憾的是,有些时候,仅仅借助一个栈,不能实现对数组的完全排序。例如给定数组2,1,3,借助一个栈,能获得的字典序最大的出栈序列是3,1,2: 2入栈;1入栈;3入栈;3出栈;1出栈;2出栈。 请你借助一个栈,对一个给定的数组按照出栈顺序进行从大到小排序。当无法完全排序时,请输出字典序最大的出栈序列。

【输入格式】

输入共 行。
第一行包含一个整数 ,表示入栈序列长度。
第二行包含 个整数,表示入栈序列。输入数据保证给定的序列是 到n的全排列,即不会出现重复数字。

【输出格式】

仅一行,共 个整数,表示你计算出的出栈序列。

【样例输入】

3
2 1 3

【样例输出】

3 1 2

【数据规模与约定】

对于30% 的数据,1<=n<=1000。
对于 60%的数据, 1<=n<=100000。
对于 100%的数据, 1<=n<=1000000。

【解题报告】

要让字典序尽量大,所以最先输出的数尽量大。
然后乱搞就可以了。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000005
int n, a[N], stack[N],top=0,nowmax;
bool vis[N];

int main() 
{
    freopen("kun.in","r",stdin);
    freopen("kun.out","w",stdout);
    memset(vis,0,sizeof(vis));
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    nowmax=n;
    for(int i=1;i<=n;i++) 
    {
        stack[++top]=a[i];
        vis[a[i]]=true;
        if(nowmax==a[i]) 
        {
            top--;
            printf("%d ",a[i]);
            while(vis[nowmax]) nowmax--;
        }
        while(a[i+1]<stack[top]&&stack[top]>nowmax) 
        {
            printf("%d ",stack[top]);
            top--;
        }
    }
    return 0;
}

nan

【问题描述】

我们有一个序列,现在他里面有三个数 。我们从第三个数开始考虑:
1、第三个数是 ,所以我们在序列后面写 个 ,变成 。
2、第四个数是 ,所以我们在序列后面写 个 ,变成 。
那么你可以看到,这个序列应该是 。
如果我们设一个数 最后出现的位置为 ,那么现在我希望知道 等于多少。

【输入格式】

第一行一个整数 ,代表数据组数。
接下来 行每行一个整数 。

【输出格式】

行,每行一个整数,代表 的值。

【样例输入】

3
3
10
100000

【样例输出】

11
217
507231491

【数据规模与约定】

对于 的数据,1<=x<=1000 。
对于 的数据,1<=x<=1000000 。
对于 的数据, 1<=x<=1e9,1<=T<=2000。

【解题报告】

60%是这么做的:
首先打表找出last和last(last) (这里只写10组):
x ——————1 2 3 4 5 6 7 8 9 10

a[x] —————1 2 2 3 3 4 4 4 5 5

last(x)————1 3 5 8 11 15 19 23 28 33

last(last(x)) ——1 5 11 23 38 62 90 122 167 217

算last(last(x))-last(last(x-1)):

last(last(x))-last(last(x-1)) 1  4  6  12  15  24  28  32  45  50

又可以写成: 1*1 2*2 2*3 3*4 3*5 4*6 4*7 4*8 4*9 5*10

可以看出,后面的数1~10就等于i,而前面的数刚好就是a[x]
于是预处理的时候就将各个分块(a[x]相同的为一块)的left和right保存起来,这样依次用等差序列求和公式就能得出ans了
至于n在中间的情况(如last(last(7)),就用a[4]的right和n(7)做比较,n小则只加上left到n的等差序列,这样就可以过60%的数据

至于100%的数据,要用到前缀和和二分的方法:前缀和求前面连续分块的和,二分求出离n最近的a[rightn].right的编号rightn,
再加上后面的等差序列(同上),可以算出大约第130万个分块的长度超过了1e9,所以二分是可行的。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 1400007
#define mod 1000000007

long long n,t;
long long last[N];
long long llst[N];
long long k[N];
struct node
{
    long long left,right;
}a[N];
long long temp=2;

int main()
{
    freopen("nan.in","r",stdin);
    freopen("nan.out","w",stdout);
    last[1]=1,last[2]=3;
    long long q=2;
    a[1].left=a[1].right=1;
    a[2].left=2;a[2].right=3;
    for(long long i=3;i<=1400000;i++)
    {
        last[i]=last[i-1]+q;
        a[i].right=last[i];
        a[i].left=last[i-1]+1;
        if(i>=last[temp])
        {
            q++;temp++;
        }
    }
    for(long long i=1;i<=1400000;i++)
    {
        k[i]=k[i-1]+(a[i].right-a[i].left+1)*i*(a[i].left+a[i].right)/2%mod;
        k[i]%=mod;
    }
    long long ans=0;
    for(scanf("%I64d",&t);t;--t)
    {
        long long n;scanf("%I64d",&n);
        long long l=1,r=1400000;
        while(l<=r)
        {
            long long mid=(l+r)>>1;
            if(a[mid].right<n)l=mid+1;
            else r=mid-1;
        }
        ans=k[r];
        ans+=(r+1)*(n-a[r].right)*(a[r+1].left+n)/2%mod;
        printf("%I64d\n",ans%mod);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值