2018.09.15模拟总结(T1,T3)

过了一周,终于迎来了第二次模拟(这不是期待的语气),看第一周毒瘤程度,我就觉得接下来的模拟只能更毒瘤。

 

花了10多分钟读完了三道题,觉得暴力还是挺好写的,然后在每一道题都思索那么几分钟后,觉得还是写暴力靠谱一些……不过还好,一个半点就把暴力都写完了。

然后我就接着想正解……

T1 game

  这道题刚开始就觉得是二维线段树,然后瞟了一眼数据范围:1e6 * 1e6,于是就犹豫了。就一直在想能不能行和列各只开一个线段树,最终反正是没想出来,于是开始优化我的暴力。推了半天发现了一个惊天秘密:就是修改的顺序是可以颠倒的:于是我就先O(n)处理了所有行的修改,然后对于所有列的修改,再暴力的O(n2)修改:所以说,只要这个数据行的修改越多,我就越容易拿掉80的点……后来经过【地表最强】lsy的山寨数据测了一下,发现还是稳稳的40分……

  暴力以及考场心路历程就说到这吧,该讲讲正解是啥咧。

  嗯……概括一下,就是这题没A就是数列没学好。

  我们开一个lzy_rol[n], lzy_lin[m]两个标记数组,记录每一行或是每一列要乘上多少。假设我们没有列的操作,那么一列看成一个数,整张图就是一个长度为m的等差数列,不想O(1)的话O(m)就能求出来。现在加上了列的操作。其实就是对于每一个数ai乘上了一个lzy_lin[i],只要按顺序算到ai,然后答案加上ai * lzy_lin[i]就行了…………

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const ll mod = 1e9 + 7;
21 const int maxn = 1e6 + 5;
22 inline ll read()
23 {
24     ll ans = 0;
25     char ch = getchar(), last = ' ';
26     while(!isdigit(ch)) {last = ch; ch = getchar();}
27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
28     if(last == '-') ans = -ans;
29     return ans;
30 }
31 inline void write(ll x)
32 {
33     if(x < 0) x = -x, putchar('-');
34     if(x >= 10) write(x / 10);
35     putchar(x % 10 + '0');
36 }
37 
38 int n, m, q; 
39 char c[5];
40 ll lzy_rol[maxn], lzy_lin[maxn]; 
41 ll s = 0, d = 0, sum = 0;
42 
43 ll getnum(int x, int i)
44 {
45     return ((ll)(i - 1) * m + x) % mod;
46 }
47 
48 int main()
49 {
50     freopen("game.in", "r", stdin);
51     freopen("game.out", "w", stdout);
52     n = read(); m = read(); q = read();
53     for(int i = 1; i < maxn; ++i) lzy_rol[i] = lzy_lin[i] = 1;
54     for(int i = 1; i <= q; ++i)
55     {
56         scanf("%s", c); int x = read(); ll k = read();
57         if(c[0] == 'R') lzy_rol[x] *= k, lzy_rol[x] %= mod;    
58         else lzy_lin[x] *= k, lzy_lin[x] %= mod;
59     }
60     for(int i = 1; i <= n; ++i)
61     {
62         s += getnum(1, i) * lzy_rol[i]; s %= mod;
63         d += lzy_rol[i]; d %= mod;
64     }                            //s:首项,d:公差 
65     for(rg int i = 1; i <= m; ++i)        
66     {
67         sum += lzy_lin[i] * s; sum %= mod;
68         s += d; s %= mod;
69     }
70     write(sum); enter;
71     return 0;
72 }
View Code

T2 jump

  在考场上我第一反应就是20分暴力,然后chuachuachua15分钟就写完了。于是就直接去搞第三题的暴力了。

  第三题暴力写完后又回来看这道题,然后就想到了一个50分的想法:先求出一个跳房子的周期,然后移动每一次k步,只要对周期取模即可。至于修改,那就是暴利修改啦。然而时间不是很够,于是就没写完。

  至于正解,我到现在还不是很懂。学姐写的是线段树维护置换。然后正解是一个很奇特的算法,不过这俩都没听懂……据说路由器还有一个更简单的做法,然而没人跟我讲啊……

T3 sequence

  我上文已经说过了,当时也是写的暴利。而且还没想到比较正常的O(n2)暴利,自己搞了一个很奇葩的算法:就是先预处理每一个数出现的位置,然后统计当前查询区间没出现的数,然后利用这些数字不断扩大查询区间,直到区间最大值减最小值等于区间长度为止。

  正解其实跟我的暴利有点像:要维护两个线段树:第一个是区间最值,第二个是在值域区间中出现位置的最值(所以说,RMQ其实就行了,而且常数更小)。

  然后我们要递归的查询:先在询问区间[L, R]中查询最小最大值Min, Max.然后在第二个线段树的区间[Min, Max]中查询出现位置的最值Minl, Minr,再换到第一个线段树在[Minl, Minr]中查询数的最大最小值……直到Max - Min = R - L,这时候的L, R 就是答案。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 1e5 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, m, a[maxn];
 38 
 39 struct Tree
 40 {
 41     int a[maxn];
 42     int l[maxn << 2], r[maxn << 2], Min[maxn << 2], Max[maxn << 2];
 43     Tree()
 44     {
 45         Mem(a, 0);
 46         Mem(l, 0); Mem(r, 0);
 47         Mem(Min, 0); Mem(Max, 0);
 48     }
 49     void build(int L, int R, int now)
 50     {
 51         l[now] = L; r[now] = R;
 52         if(L == R) {Min[now] = Max[now] = a[L]; return;}
 53         int mid = (L + R) >> 1;
 54         build(L, mid, now << 1);
 55         build(mid + 1, R, now << 1 | 1);
 56         Min[now] = min(Min[now << 1], Min[now << 1 | 1]);
 57         Max[now] = max(Max[now << 1], Max[now << 1 | 1]);        
 58     }
 59     int query(int L, int R, int now, bool flg)
 60     {
 61         if(L == l[now] && R == r[now]) return flg ? Max[now] : Min[now];
 62         int mid = (l[now] + r[now]) >> 1;
 63         if(R <= mid) return query(L, R, now << 1, flg);
 64         else if(L > mid) return query(L, R, now << 1 | 1, flg);
 65         else 
 66         {
 67             int ret;
 68             if(flg) ret = max(query(L, mid, now << 1, flg), query(mid + 1, R, now << 1 | 1, flg));
 69             else ret = min(query(L, mid, now << 1, flg), query(mid + 1, R, now << 1 | 1, flg));
 70             return ret;
 71         }
 72     }
 73 }Seq, Num;
 74 
 75 #define pr pair<int, int>
 76 #define mp make_pair 
 77 #define fir first
 78 #define sec second
 79 pr Query(int L, int R)
 80 {
 81     while(1)
 82     {
 83         int Ml = Seq.query(L, R, 1, 0), Mr = Seq.query(L, R, 1, 1);
 84         if(Mr - Ml == R - L) return mp(L, R);
 85         L = Num.query(Ml, Mr, 1, 0);
 86         R = Num.query(Ml, Mr, 1, 1);
 87     }
 88 }
 89 
 90 int main()
 91 {
 92     freopen("sequence.in", "r", stdin);
 93     freopen("sequence.out", "w", stdout);
 94     n = read();
 95     for(int i = 1; i <= n; ++i) a[i] = read();
 96     for(int i = 1; i <= n; ++i) Seq.a[i] = a[i], Num.a[a[i]] = i;
 97     Seq.build(1, n, 1); Num.build(1, n, 1); 
 98     m = read();
 99     for(int i = 1; i <= m; ++i)
100     {
101         int L = read(), R = read();
102         pr p = Query(L, R);
103         write(p.fir); space; write(p.sec); enter;
104     }
105     return 0;
106 }
View Code

不过这个线段树好像常数很大,然后换了数据后后面几个点就TLE了……那真正的正解就不太懂了。

  总的来说今天的考试还是挺顺利的,暴利一个半点很愉快的就写完了。自己估分100左右,结果出成绩后刚好102,顿时觉得自己相当nb。反正想拿的分都拿到了,就没什么遗憾。

原文地址:https://www.cnblogs.com/mrclr/p/9651284.html