NOIP模拟测试13

C题 60分扔扔扔,太遗憾了:(

Problem A:矩阵游戏

啥也不说了,先打了个40暴力。

然后看到数据范围,想到肯定可以颓柿子。开始颓颓颓。

一个点$(i, j)$的数字是$(i - 1)m + j$。最后的答案是$\sum \limits_{i=1}^n \sum \limits_{j=1}^m R_i S_j [(i-1)m + j]$.

继续化简:

原式 = $\sum \limits_{i=1}^n R_i \sum \limits_{j=1}^m S_j [(i-1)m + j]$ = $\sum \limits_{i=1}^n R_i  ((i-1) \sum \limits_{j=1}^m mS_j + \sum \limits_{j=1}^m jS_j)$

$\sum \limits_{j=1}^m mS_j $ 和 $ \sum \limits_{j=1}^m jS_j$都可以$O(m)$线性求,最后再$O(n)$把答案求出来,得到一个$O(n + m)$的吼算法。

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 
 4 const int N = 1000000 + 233, MOD = 1000000007;
 5 ll n, m, k, R[N], S[N], sum_s1, sum_s2, ans;
 6 
 7 signed main() {
 8     scanf("%lld%lld%lld", &n, &m, &k);
 9     for (int i = 1; i <= n; i++) R[i] = 1ll;
10     for (int i = 1; i <= m; i++) S[i] = 1ll;
11     for (int i = 1; i <= k; i++) {
12         char op[10]; ll x, y;
13         scanf("%s%lld%lld", op, &x, &y);
14         if (op[0] == 'R') {
15             (R[x] *= y) %= MOD;
16         } else {
17             (S[x] *= y) %= MOD;
18         }
19     } 
20     for (int i = 1; i <= m; i++) {
21         sum_s1 = (sum_s1 + i * S[i]) % MOD;
22         sum_s2 = (sum_s2 + m * S[i]) % MOD;
23     }
24     for (int i = 1; i <= n; i++) {
25         ans = (ans + R[i] * ((sum_s1 + ((ll) i - 1ll) * sum_s2) % MOD) % MOD) % MOD;
26     }
27     return !printf("%lld\n", ans);
28 }
A

 Problem B:跳房子

考试的时候直接码了个一步一步挪的暴力。

其实看出来有循环节了,但写起来细节太多,真心码不动Orz

TKJ的这种方法很易于理解,而且很好码。

我们只看每一列,每次移动等价于一次置换。

开个线段树去维护置换,把区间+重载一下更方便。

其他部分都按普通线段树写就行。

  1 #include <bits/stdc++.h>
  2 
  3 const int N = 2005;
  4 int n, m, q, val[N][N], x = 1, y = 1;
  5 
  6 class SegTree {
  7 private:
  8     struct Node {
  9         int nxt[N];
 10         friend Node operator *(Node a, Node b) {
 11             Node ret;
 12             for (int i = 1; i <= n; i++)
 13                 ret.nxt[i] = b.nxt[a.nxt[i]];
 14             return ret;
 15         }
 16     } t[N << 2];
 17     void build(int p, int l, int r) {
 18         if (l == r) {
 19             for (int i = 1; i <= n; i++) {
 20                 int yy = l == m ? 1 : l + 1;
 21                 int x_1 = i == n ? 1 : i + 1;
 22                 int x_2 = i == 1 ? n : i - 1;
 23                 int x_3 = i;
 24                 t[p].nxt[i] = val[x_1][yy] > val[x_2][yy] &&
 25                     val[x_1][yy] > val[x_3][yy] ? x_1 :
 26                     val[x_2][yy] > val[x_3][yy] ? x_2 : x_3;
 27             }
 28             return;
 29         }
 30         int mid = (l + r) >> 1;
 31         build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
 32         t[p] = t[p << 1] * t[p << 1 | 1];
 33     }
 34     void _change(int p, int l, int r, int x) {
 35         if (l == r) {
 36             for (int i = 1; i <= n; i++) {
 37                 int yy = l == m ? 1 : l + 1;
 38                 int x_1 = i == n ? 1 : i + 1;
 39                 int x_2 = i == 1 ? n : i - 1;
 40                 int x_3 = i;
 41                 t[p].nxt[i] = val[x_1][yy] > val[x_2][yy] &&
 42                     val[x_1][yy] > val[x_3][yy] ? x_1 :
 43                     val[x_2][yy] > val[x_3][yy] ? x_2 : x_3;
 44             }
 45             return;
 46         } 
 47         int mid = (l + r) >> 1;
 48         if (x <= mid) _change(p << 1, l, mid, x);
 49         else _change(p << 1 | 1, mid + 1, r, x);
 50         t[p] = t[p << 1] * t[p << 1 | 1];
 51     }
 52     Node _query(int p, int l, int r, int L, int R) {
 53         if (l == L && r == R) return t[p];
 54         int mid = (L + R) >> 1;
 55         if (r <= mid) return _query(p << 1, l, r, L, mid);
 56         else if (l > mid) return _query(p << 1 | 1, l, r, mid + 1, R);
 57         else return _query(p << 1, l, mid, L, mid) * 
 58             _query(p << 1 | 1, mid + 1, r, mid + 1, R);
 59     }
 60     Node _qpow(Node x, int b) {
 61         Node ret;
 62         for (int i = 1; i <= n; i++) ret.nxt[i] = i;
 63         for (; b; b >>= 1, x = x * x)
 64             if (b & 1) ret = ret * x;
 65         return ret;
 66     }
 67 public:
 68     SegTree() {
 69         build(1, 1, m);
 70     }
 71     Node query(int l, int r) {
 72         return _query(1, l, r, 1, m);
 73     }
 74     void change(int x) {
 75         _change(1, 1, m, x);
 76     }
 77     Node qpow(int b) {
 78         return _qpow(t[1], b);
 79     }
 80 };
 81 
 82 signed main() {
 83     scanf("%d%d", &n, &m);
 84     for (int i = 1; i <= n; i++)
 85         for (int j = 1; j <= m; j++)
 86             scanf("%d", &val[i][j]);
 87     static SegTree *Gekoo = new SegTree();
 88     scanf("%d", &q);
 89     for (int i = 1; i <= q; i++) {
 90         char op[10]; int k, a, b, e;
 91         scanf("%s", op);
 92         if (op[0] == 'm') {
 93             scanf("%d", &k);
 94             if (!k) {
 95                 printf("%d %d\n", x, y);
 96                 continue;
 97             }
 98             if (y + k - 1 <= m) {
 99                 x = Gekoo->query(y, y + k - 1).nxt[x];
100                 y = (y + k - 1 == m) ? 1 : y + k;
101             } else {
102                 k -= m - y + 1;
103                 x = Gekoo->query(y, m).nxt[x], y = 1;
104                 if (k / m)
105                     x = Gekoo->qpow(k / m).nxt[x];
106                 if (k % m)
107                     x = Gekoo->query(y, y + k % m - 1).nxt[x], 
108                       y += k % m;
109             }
110             printf("%d %d\n", x, y);
111         } else {
112             scanf("%d%d%d", &a, &b, &e);
113             val[a][b] = e;
114             Gekoo->change(b == 1 ? m : b - 1);
115         }
116     }
117     return 0;
118 }
B

Problem C:优美序列

这题很像奇袭,一个区间是优美的,等价于$r - l = max_{[l, r]} - min_{[l, r]}$.

可以用ST表暴力做:查询出询问区间的max和min,把它们作为端点继续查,直到不再改变,可以得84pt,不详谈,直接贴代码。

 1 #include <bits/stdc++.h>
 2 
 3 const int N = 100005;
 4 int n, m, pos[N], ori[N]; 
 5 int posmn[N][20], posmx[N][20], orimn[N][20], orimx[N][20], lg[N];
 6 
 7 inline int read() {
 8     int a = 0; char c = getchar();
 9     while (!isdigit(c)) c = getchar();
10     while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
11     return a;
12 }
13 
14 inline int query_orimx(int l, int r) {
15     int t = lg[r - l + 1];
16     return std::max(orimx[l][t], orimx[r-(1<<t)+1][t]);
17 }
18 
19 inline int query_orimn(int l, int r) {
20     int t = lg[r - l + 1];
21     return std::min(orimn[l][t], orimn[r-(1<<t)+1][t]);
22 }
23 
24 inline int query_posmx(int l, int r) {
25     int t = lg[r - l + 1];
26     return std::max(posmx[l][t], posmx[r-(1<<t)+1][t]);
27 }
28 
29 inline int query_posmn(int l, int r) {
30     int t = lg[r - l + 1];
31     return std::min(posmn[l][t], posmn[r-(1<<t)+1][t]);
32 }
33 
34 signed main() {
35     n = read();
36     for (int i = 1; i <= n; i++) {
37         scanf("%d", ori + i);
38         pos[ori[i]] = i;
39     }
40     for (int i =  1; i <= n; i++)
41         posmx[i][0] = posmn[i][0] = pos[i],
42         orimx[i][0] = orimn[i][0] = ori[i];
43     for (register int j = 1; (1 << j) <= n; j++)
44         for (register int i = 1; i <= n - (1 << j) + 1; i++)
45             posmx[i][j] = std::max(posmx[i][j-1], posmx[i+(1<<(j-1))][j-1]),
46             posmn[i][j] = std::min(posmn[i][j-1], posmn[i+(1<<(j-1))][j-1]),
47             orimx[i][j] = std::max(orimx[i][j-1], orimx[i+(1<<(j-1))][j-1]),
48             orimn[i][j] = std::min(orimn[i][j-1], orimn[i+(1<<(j-1))][j-1]);
49     for (int i = 0, t = 0; i <= n; i++) {
50         if (i >= (1 << (t + 1))) t++;
51         lg[i] = t;
52     }
53     m = read();
54     while (m--) {
55         int l, r, pl = 0, pr = 0, omx, omn;
56         l = read(), r = read();
57         bool flg = 0;
58         while (l != pl || r != pr) {
59             if (flg) l = pl, r = pr;
60             omx = query_orimx(l, r), omn = query_orimn(l, r);
61             pl = query_posmn(omn, omx), pr = query_posmx(omn, omx);
62             flg = 1;
63         }
64         printf("%d %d\n", pl, pr);
65     }
66     return 0;
67 }
ST

正解是分治,不提了,这里写的是DKY的线段树优化建图方法

由上式,相邻两个数i, i + 1可以加入优美区间,仅当区间中出现了$[val_i, val_{i + 1}]$的所有值。

用边连接节点,表示限制关系,用点向区间连边显然可以线段树优化。

Tarjan缩点求SCC,DFS求出存在每个点所需要的区间。

用线段树可以维护。原因不说了,显然。

最后的答案就是区间内所有点对应区间的并。

  1 #include <bits/stdc++.h>
  2 
  3 const int INF = 0x3f3f3f3f, N = 400000 + 233;
  4 int pos[N], n, val[N], m;
  5 std::vector<int> G1[N], G2[N];
  6 bool vis[N];
  7 
  8 struct Node {
  9     int l, r;
 10     Node() {l = INF, r = -1;}
 11     Node(int ll, int rr) {l = ll, r = rr;}
 12     friend Node operator +(Node x, Node y) {
 13         return Node(std::min(x.l, y.l), std::max(x.r, y.r));    
 14     }
 15 };
 16 
 17 class SegTree {
 18 private:
 19     struct Tree {
 20         Node t[N];
 21         void _change(int p, int L, int R, int x, Node v) {
 22             if (L == R) return (void) (t[p] = v);
 23             int mid = (L + R) >> 1;
 24             if (x <= mid) _change(p << 1, L, mid, x, v);
 25             else _change(p << 1 | 1, mid + 1, R, x, v);
 26             t[p] = t[p<<1] + t[p<<1|1];
 27         }
 28         Node _query(int p, int L, int R, int l, int r) {
 29             if (l <= L && r >= R) return t[p];
 30             int mid = (L + R) >> 1;
 31             if (r <= mid) return _query(p << 1, L, mid, l, r);
 32             else if (l > mid) return _query(p << 1 | 1, mid + 1, R, l, r);
 33             else return _query(p << 1, L, mid, l, mid) +
 34                 _query(p << 1 | 1, mid + 1, R, mid + 1, r);
 35         }
 36     } seg[2];
 37     void _build(int p, int L, int R) {
 38         if (L == R) return (void) (pos[L] = p);
 39         int mid = (L + R) >> 1;
 40         _build(p << 1, L, mid), _build(p << 1 | 1, mid + 1, R);
 41         G1[p].push_back(p << 1), G1[p].push_back(p << 1 | 1);
 42     }
 43     void _addedge(int p, int L, int R, int l, int r, int q) {
 44         if (l <= L && r >= R) return (void) (G1[q].push_back(p));
 45         int mid = (L + R) >> 1;
 46         if (l <= mid) _addedge(p << 1, L, mid, l, r, q);
 47         if (r > mid) _addedge(p << 1 | 1, mid + 1, R, l, r, q);
 48     }
 49 public:
 50     SegTree() {
 51         _build(1, 1, n);
 52     }
 53     Node query(int id, int l, int r) {
 54         return seg[id]._query(1, 1, n, l, r);
 55     }
 56     void addedge(int l, int r, int q) {
 57         _addedge(1, 1, n, l, r, q);
 58     }
 59     void change(int id, int x, Node v) {
 60         seg[id]._change(1, 1, n, x, v);
 61     }
 62 };
 63 
 64 int dfn[N], low[N], stk[N], num, tp, scc[N], tot;
 65 bool ins[N];
 66  
 67 void Tarjan(int x) {
 68     dfn[x] = low[x] = ++num;
 69     ins[stk[++tp] = x] = 1;
 70     for (auto y : G1[x]) {
 71         if (!dfn[y]) {
 72             Tarjan(y);
 73             low[x] = std::min(low[x], low[y]);
 74         } else if (ins[y]) 
 75             low[x] = std::min(low[x], dfn[y]);
 76     }
 77     if (low[x] == dfn[x]) {
 78         int y; ++tot;
 79         do {
 80             ins[y = stk[tp--]] = 0, scc[y] = tot;
 81         } while (x != y);
 82     }
 83 }
 84 
 85 Node t1[N], t2[N];
 86 
 87 void dfs(int x) {
 88     if (vis[x]) return;
 89     vis[x] = 1;
 90     for (auto y : G2[x]) {
 91         dfs(y);
 92         t2[x] = t2[x] + t2[y]; 
 93     }
 94 }
 95 
 96 signed main() {
 97     scanf("%d", &n);
 98     for (int i = 1; i <= n; i++)
 99         scanf("%d", &val[i]);
100     static SegTree *Gekoo = new SegTree();
101     for (int i = 1; i <= n; i++)
102         Gekoo->change(0, val[i], {i, i});
103     for (int i = 2; i <= n; i++) {
104         int l = std::min(val[i - 1], val[i]), r = std::max(val[i - 1], val[i]);
105         t1[pos[i]] = Gekoo->query(0, l, r);
106         Gekoo->addedge(t1[pos[i]].l + 1, t1[pos[i]].r, pos[i]);
107     }
108     for (int i = 1; i < N; i++)
109         if (!dfn[i]) Tarjan(i);
110     for (int x = 1; x < N; x++)
111         for (auto y : G1[x])
112             if (scc[x] != scc[y]) G2[scc[x]].push_back(scc[y]);
113     for (int i = 1; i < N; i++)
114         t2[scc[i]] = t2[scc[i]] + t1[i];
115     for (int i = 1; i <= tot; i++) dfs(i);
116     for (int i = 2; i <= n; i++)
117         Gekoo->change(1, i, t2[scc[pos[i]]]);
118     scanf("%d", &m);
119     for (int i = 1, x, y; i <= m; i++) {
120         scanf("%d%d", &x, &y);
121         if (x == y) {
122             printf("%d %d\n", x, y);
123         } else {
124             Node ans = Gekoo->query(1, x + 1, y);
125             printf("%d %d\n", ans.l, ans.r);
126         }
127     }
128     return 0;    
129 }
C

 

posted @ 2019-08-06 08:37  Gekoo  阅读(185)  评论(0编辑  收藏  举报