2018.8.17 省选模拟赛
Posted yyf0309
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018.8.17 省选模拟赛相关的知识,希望对你有一定的参考价值。
*注意:这套题目应版权方要求,不得公示题面。
Problem A 林先森管仓库
题目大意
要求维护一个数组,支持:
- 将一段连续的位置上的数变为一个编号
- 将等于给定编号的数变为0
- 将数组中非零数向左移动。
数据范围小得可怜。普及组?
数据范围大点就直接上平衡树维护0段,三操作时间复杂度均摊有保证。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cstdio> 5 #include <queue> 6 using namespace std; 7 typedef bool boolean; 8 9 int n, m; 10 int *ar; 11 12 inline void init() { 13 scanf("%d%d", &n, &m); 14 ar = new int[(m + 1)]; 15 memset(ar, 0, sizeof(int) * (m + 1)); 16 } 17 18 int cnt = 0; 19 20 void put(int size) { 21 int spare = 0; 22 for (int i = 1; i <= m; i++) { 23 if (ar[i]) 24 spare = 0; 25 else 26 spare++; 27 if (spare == size) { 28 cnt++; 29 for (int j = 0; j < size; j++) 30 ar[i - j] = cnt; 31 printf("%d ", cnt); 32 return; 33 } 34 } 35 puts("cannot put."); 36 } 37 38 void get(int id) { 39 boolean aflag = false; 40 for (int i = 1; i <= m; i++) 41 if (ar[i] == id) 42 aflag = true, ar[i] = 0; 43 if (!aflag) 44 puts("cannot get."); 45 } 46 47 void arrange() { 48 queue<int> que; 49 for (int i = 1; i <= m; i++) 50 if (ar[i]) 51 que.push(ar[i]); 52 memset(ar, 0, sizeof(int) * (m + 1)); 53 int i = 1; 54 while (!que.empty()) { 55 ar[i++] = que.front(); 56 que.pop(); 57 } 58 } 59 60 inline void solve() { 61 char str[10]; 62 int x; 63 while (n--) { 64 scanf("%s", str); 65 if (str[0] == ‘p‘) { 66 scanf("%d", &x); 67 put(x); 68 } else if (str[0] == ‘g‘) { 69 scanf("%d", &x); 70 get(x); 71 } else if (str[0] == ‘a‘) 72 arrange(); 73 } 74 } 75 76 int main() { 77 freopen("warehouse.in", "r", stdin); 78 freopen("warehouse.out", "w", stdout); 79 init(); 80 solve(); 81 return 0; 82 }
Problem B 林先森染地毯
题目大意
有一个$n imes m$的网格图,可以用$K$种颜色进行染色。对于任意相邻两个格子之间都有一个限制,要求这两个格子的颜色相同或者不同。问是否存在一种方案使得至少$frac{4}{3}$的要求被满足。如果是输出方案。
$K = 1$的时候直接数相等限制的个数。
$K > 1$的时候考虑只用2种颜色进行染色。
我先讲一下我考场用的随机做法。
强行让第一行和第一列的限制满足,对于其他格子的颜色随机让它满足它和它的上面相邻的格子之间的列限制和它和它左边相邻的格子之间行限制中任意一个。然后满足的要求的个数。感受一下。我能保证至少一半的要求能被满足,剩下的四分之一是随机出来的。于是OJ上这道题跑得最快的程序是我的,内存消耗最小的程序是我的,代码长度最短的程序也是我的。(Excuse me? Jode 2.0?)
标算的做法其实也很简单。它先把长的作为行。然后强行先让行内限制满足,然后考虑行间限制。如果两行间的限制满足的少于一半,那么就反转当前一行的染色。
然后orz XTC 6kb假贪心爆过原数据和我考场上出的数据。然而拿到他的程序后,3分钟内被我的数据生成器干掉,被jmr人工卡掉。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 using namespace std; 5 typedef bool boolean; 6 7 const int N = 1005; 8 9 int n, m, k; 10 int lim; 11 char lr[N][N], cr[N][N]; 12 boolean col[N][N]; 13 14 inline void init() { 15 scanf("%d%d%d", &n, &m, &k); 16 for (int i = 1; i <= n; i++) { 17 if (m > 1) 18 scanf("%s", lr[i] + 2); 19 if (i < n) 20 scanf("%s", cr[i + 1] + 1); 21 } 22 lim = n * (m - 1) + (n - 1) * m; 23 lim = (lim * 3 + 3) >> 2; 24 } 25 26 int check(int a, int b, char c) { 27 if (a == b && c == ‘E‘) 28 return 1; 29 if (a != b && c == ‘N‘) 30 return 1; 31 return 0; 32 } 33 34 const int T = 10; 35 void check() { 36 col[1][1] = false; 37 for (int i = 2; i <= m; i++) 38 col[1][i] = ((lr[1][i] == ‘E‘) ? (col[1][i - 1]) : (!col[1][i - 1])); 39 for (int i = 2; i <= n; i++) 40 col[i][1] = ((cr[i][1] == ‘E‘) ? (col[i - 1][1]) : (!col[i - 1][1])); 41 42 int cnt = m + n - 2; 43 for (int i = 2; i <= n; i++) 44 for (int j = 2; j <= m; j++) { 45 int satis = rand() & 1; 46 if (!satis) 47 col[i][j] = ((lr[i][j] == ‘E‘) ? (col[i][j - 1]) : (!col[i][j - 1])); 48 else 49 col[i][j] = ((cr[i][j] == ‘E‘) ? (col[i - 1][j]) : (!col[i - 1][j])); 50 51 cnt += check(col[i][j], col[i][j - 1], lr[i][j]); 52 cnt += check(col[i][j], col[i - 1][j], cr[i][j]); 53 } 54 55 if (cnt >= lim) { 56 puts("YES"); 57 for (int i = 1; i <= n; i++, putchar(‘ ‘)) 58 for (int j = 1; j <= m; j++) 59 putchar(‘1‘ + ((col[i][j]) ? (1) : (0))), putchar(‘ ‘); 60 exit(0); 61 } 62 } 63 64 inline void solve() { 65 if (k == 1) { 66 int cnt = 0; 67 for (int i = 1; i <= n; i++) 68 for (int j = 2; j <= m; j++) 69 cnt += (lr[i][j] == ‘E‘); 70 for (int i = 2; i <= n; i++) 71 for (int j = 1; j <= m; j++) 72 cnt += (cr[i][j] == ‘E‘); 73 if (cnt >= lim) { 74 puts("YES"); 75 for (int i = 1; i <= n; i++, putchar(‘ ‘)) 76 for (int j = 1; j <= m; j++) 77 putchar(‘1‘), putchar(‘ ‘); 78 } else { 79 puts("NO"); 80 } 81 } else { 82 for (int t = 1; t <= 10; t++) 83 check(); 84 puts("NO"); 85 } 86 } 87 88 int main() { 89 freopen("carpet.in", "r", stdin); 90 freopen("carpet.out", "w", stdout); 91 srand(233u); 92 init(); 93 solve(); 94 return 0; 95 }
Problem C 林先森学数学
题目大意
给定一个数组,要求支持:
- 给定$l, r$,对于任意整数$i$满足$lleqslant i leqslant r$,将$a_{i}$替换为$a_{i}^{3}$。
- 区间求和
答案对95542721取模。
天真的我以为这是一道十分正(毒)经(瘤)的数据结构题。然后考场上就没做出来。
遇到这种奇奇怪怪的质因数就该打个表,或者质因数分解它的phi。
——Joker
大概也是这样的吧。(先设$M = 95542721$)
我讲讲比较文(套)艺(路)的方法。
考虑模$M$意义下的原根$g$,然后解一个方程:
$g^{3^{x}}equiv g pmod{M}$
然后直接上原根的定义:
$3^{x}equiv 1pmod{varphileft ( M ight )}$
啥?你不想写BSGS?可以手算[手动滑稽]。
然后可以的得到$x$的一个整数解$x = 48$。
因为不断将原根立方的循环节是48,原根的次幂可以表示整个既约剩余系。(0的循环节显然。)因此$0$到$M$的循环节都是48的约数(这个显然)。
然后秒变数据结构智障题。
开线段树,维护区间内每个数进行$0$到$47$次操作1后区间的和。标记记录区间内每个数同时进行操作1的次数。
显然信息可以下方或者合并,标记可以合并。这道题就切掉了。
对于另外一方法就是打表。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 using namespace std; 5 typedef bool boolean; 6 7 const int M = 95542721, C = 48; 8 9 int add(int a, int b, int M) { 10 int rt = a + b; 11 if (rt >= M) 12 return rt - M; 13 return rt; 14 } 15 16 typedef class SegTreeNode { 17 public: 18 int cir, lazy; 19 int sums[C + 1]; 20 SegTreeNode *l, *r; 21 22 SegTreeNode() { } 23 24 void pushUp() { 25 if (lazy) 26 pushDown(); 27 cir = 0; 28 for (int i = 0; i < C; i++) 29 sums[i] = add(l->sums[add(i, l->cir, C)], r->sums[add(i, r->cir, C)], M); 30 } 31 32 void pushDown() { 33 l->cir = add(l->cir, lazy, C); 34 r->cir = add(r->cir, lazy, C); 35 l->lazy = add(l->lazy, lazy, C); 36 r->lazy = add(r->lazy, lazy, C); 37 lazy = 0; 38 } 39 }SegTreeNode; 40 41 SegTreeNode pool[500005]; 42 SegTreeNode* top = pool; 43 44 SegTreeNode* newnode() { 45 return top++; 46 } 47 48 typedef class SegTree { 49 public: 50 SegTreeNode* rt; 51 52 SegTree() { } 53 54 void build(SegTreeNode*& p, int l, int r, int *ar) { 55 p = newnode(); 56 if (l == r) { 57 int x = ar[l] % M; 58 for (int i = 0; i < C; i++, x = (x * 1ll * x) % M * x % M) 59 p->sums[i] = x; 60 return; 61 } 62 int mid = (l + r) >> 1; 63 build(p->l, l, mid, ar); 64 build(p->r, mid + 1, r, ar); 65 p->pushUp(); 66 } 67 68 int query(SegTreeNode*& p, int l, int r, int ql, int qr) { 69 if (l == ql && r == qr) 70 return p->sums[p->cir]; 71 if (p->lazy) 72 p->pushDown(); 73 int mid = (l + r) >> 1; 74 if (qr <= mid) 75 return query(p->l, l, mid, ql, qr); 76 if (ql > mid) 77 return query(p->r, mid + 1, r, ql, qr); 78 return add(query(p->l, l, mid, ql, mid), query(p->r, mid + 1, r, mid + 1, qr), M); 79 } 80 81 void modify(SegTreeNode*& p, int l, int r, int ql, int qr) { 82 if (l == ql && r == qr) { 83 p->cir = add(p->cir, 1, C); 84 p->lazy = add(p->lazy, 1, C); 85 return; 86 } 87 if (p->lazy) 88 p->pushDown(); 89 int mid = (l + r) >> 1; 90 if (qr <= mid) 91 modify(p->l, l, mid, ql, qr); 92 else if (ql > mid) 93 modify(p->r, mid + 1, r, ql, qr); 94 else { 95 modify(p->l, l, mid, ql, mid); 96 modify(p->r, mid + 1, r, mid + 1, qr); 97 } 98 p->pushUp(); 99 } 100 }SegTree; 101 102 int n, m; 103 int *ar; 104 SegTree st; 105 106 inline void init() { 107 scanf("%d%d", &n, &m); 108 ar = new int[(n + 1)]; 109 for (int i = 1; i <= n; i++) 110 scanf("%d", ar + i); 111 } 112 113 inline void solve() { 114 char str[10]; 115 int l, r; 116 st.build(st.rt, 1, n, ar); 117 while (m--) { 118 scanf("%s%d%d", str, &l, &r); 119 if (str[0] == ‘q‘) 120 printf("%d ", st.query(st.rt, 1, n, l, r)); 121 else 122 st.modify(st.rt, 1, n, l, r); 123 } 124 } 125 126 int main() { 127 init(); 128 solve(); 129 return 0; 130 }
以上是关于2018.8.17 省选模拟赛的主要内容,如果未能解决你的问题,请参考以下文章