Codeforces 380A - Sereja and Prefixes

原题地址:http://codeforces.com/problemset/problem/380/A

让期末考试整的好久没有写题, 放假之后由于生病也没怎么做,新年的第一场CF也不是那么在状态,只过了div2的前两道题,第三题想到算法但是太困了没写对,刚刚把第三题A了很兴奋,赶快过来把解题报告写了。不得不说这道题还算有点水平

题目大意:

维护一个初始为空的序列,支持两种操作,共 m 个(1 <= m <= 10 ^ 5):

1 将一个数插入到数列的尾端(插入的数不大于10 ^ 5)

2 将数列的前 l 位复制 c 次,贴到数列的尾端(1 <= l <= 10 ^ 5, 1 <= c <= 10 ^ 4)

然后进行 n (1 <= n <= 10 ^ 5)次查询,每次给出一个数 x ,输出最终数列的第x位(所有查询数满足严格递增)

题目满足数据合法

题目分析:

这道题给人的第一反应是数据规模很大,直接模拟生成数列不仅会超时,而且会爆系统内存(最多大概可以有 10 ^ 9个数),所以我们着手去想其他的解题思路。

首先想到的是处理出每一个操作所生成的数列的位置(包括起始位置和终止位置)然后按照查询给出的位置 x 在操作序列中二分查找出生成包含 x 这一段的操作的位置,根据这个操作去计算位置x上的值。但是这样要考虑到一件事,如果生成 x 的操作是操作 2 , 那么由于操作 2 是复制当前数列的前l个数,而当前数列仍旧是未知的,也就是说我们还是需要再在前面的操作中二分,知道查到一个由操作一生成的点。这种做法直接导致复杂度无法估计,而且递归的编程难度加大,想法有点难实现。

接下来我们从范围入手,注意到操作二只会用到当前数列的前10 ^ 5个点,而数列中的点是不会修改的,所以我们萌生了这种想法:模拟出数列的前10 ^ 5个点,然后再查找生成 x 的对应操作。这样问题就简化很多了。

模拟之后的查找有两种方案,第一种还是二分,复杂度O(n log n)没问题,但是写起来稍微麻烦。后一种方法用到了题目中查询序列的递增顺序,直接线性地从先往后扫就行,复杂度是O(n + m)

细节处理:

1. 注意边界条件和循环退出条件

2.如果当前查到的点的位置小于10 ^ 5 直接输出模拟出来的数列中的对应位置值就可以了。

3.如果查到的位置对应操作为操作1,直接输出操作一的操作数值,如果为操作二需要计算一下取模(详见代码),特别注意取模得零的情况

 1 //date 20140114
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 
 6 using namespace std;
 7 
 8 const int maxm = 100005;
 9 
10 inline int getint()
11 {
12     int ans(0); char w = getchar();
13     while('0' > w || w > '9')w = getchar();
14     while('0' <= w && w <= '9')
15     {
16         ans = ans * 10 + w - '0';
17         w = getchar();
18     }
19     return ans;
20 }
21 
22 int n, m;
23 struct build
24 {
25     int ord, x, l, c;
26     long long a, b;
27 }hell[maxm];
28 int sq[maxm];
29 
30 int main()
31 {
32     m = getint();
33     for(int i = 1; i <= m; ++i)
34     {
35         hell[i].ord = getint();
36         if(hell[i].ord == 1){
37             hell[i].x = getint(); 
38             hell[i].a = hell[i].b = hell[i - 1].b + 1LL;
39             if(hell[i].a < maxm)sq[hell[i].a] = hell[i].x;
40         }
41         else{
42             hell[i].l = getint(); hell[i].c = getint();
43             hell[i].a = hell[i - 1].b + 1LL;
44             hell[i].b = hell[i - 1].b + (long long)hell[i].l * (long long)hell[i].c;
45             if(hell[i].a < maxm)
46             {
47                 int now = hell[i].a;
48                 for(int j = 1; j <= hell[i].c && now < maxm; ++j)
49                     for(int k = 1; k <= hell[i].l && now < maxm; ++k)
50                         sq[now++] = sq[k];
51             }
52         }
53     }
54     
55     n = getint();
56     int h = 1; long long x;
57     for(int i = 1; i <= n; ++i)
58     {
59         cin >> x;
60         while(!(hell[h].a <= x && hell[h].b >= x))++h;
61         if(x <= 100000)printf("%d ", sq[x]);
62         else{
63             if((x - hell[h - 1].b) % (long long)hell[h].l != 0)
64                 cout << sq[(x - hell[h - 1].b) % (long long)hell[h].l] << ' ';
65             else cout << sq[hell[h].l] << ' ';
66         }
67     }
68     return 0;
69 }

总结:

这题就是想法和细节以及编程能力。

 

转载于:https://www.cnblogs.com/w007878/p/3519176.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值