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 }

总结:

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

 

你可能感兴趣的:(codeforces)