NOIP2012提高组解题报告
2013-06-12 10:34
375 查看
Day1T1
太长了不复制了, 看这里http://wikioi.com/problem/1197/
简单的字符串替换问题.
Day1T2
题目:http://wikioi.com/problem/1198/
给出一些二元组(Ai, Bi), 将他们按某种顺序排列. 对于每个二元组, 有一个值Fi, 计算方式是他之前所有二元组的Ai乘起来, 除以该二元组的Bi. 使得Max{F1, F2, F3 ... Fn}最小.
考虑交换相邻两个二元组的影响, 交换二元组i和二元组i+1,实际上只影响到Fi和Fi+1。
设T = A1 * A2 * ... * Ai-2 * Ai-1
方案A: Fi = T / Bi, Fi+1 =T * Ai / Bi+1
方案B: Fi = T / Bi+1, Fi+1 =T * Ai+1 / Bi
①假设1 / Bi < Ai / Bi+1, 1 / Bi+1 < Ai+1 / Bi
那么方案A优于方案B(Ai / Bi+1 < Ai+1 / Bi, Ai * Bi < Ai+1 * B i+1)
②假设1 / Bi ≥ Ai / Bi+1(Ai * Bi ≤ Bi+1)
那么1 / Bi ≤ Ai+1 / Bi, 方案A更优
③假设1 / Bi+1 ≥ Ai+1 / Bi(Ai+1 * Bi+1 ≤ Bi)
那么1 / Bi+1 ≤ Ai / Bi+1, 方案B更优
也就是说, 对于相邻两个二元组, 将A * B的较小的放在前面总能使答案更优.
实现要使用高精度, 结束.
Day1T3开车旅行
题目:http://wikioi.com/problem/1199/
对每个i点维护nextA[i], nextB[i]表示i往后A的话去哪里, B的话去哪里. 这个可以用链表来计算, 我们把所有数从小到大排序, 然后串成一个链表. 从1到n每次找Hi在链表里的前后4个判断一下, 然后把Hi从链表里删掉. 然后这样我们再用nextAB[i]表示i往后A走一次B走一次到哪里. 那么可以发现i->nextAB[i]形成了一颗树. 之后可以用树的算法做. 不过最简单的还是预处理next[i][j]表示i往后A和B都走了2^j次到哪里. 然后从大到小判2的每个次幂.
写的我太难受了, 连平时喜欢纠结的格式都没纠结成.
Day2T1同余方程
求关于 x 同余方程 ax ≡ 1 (mod b)的最小正整数解.
一句话: 用exgcd或则利用结论a^(phi(b)-1)%b.
Day2T2借教室
题目:http://wikioi.com/problem/1217/
标准解法是:让我们考虑现在有m个操作,我们先把这些操作的端点离散化一下,之后可以注意到,相邻的两个关键点之间,只有最少的哪天是有用的,那么我们可以把天数压到2m级别。然后有m个操作,我们先加入前m/2个,然后用部分和判断一下前m/2个会不会跪,会的话就在前m/2个中递归,不然就在后m/2个中递归。由于我们每次可以压天数。复杂度函数就是F(n)=n+F(n/2)=O(n).
然后我很纠结的写了个二分...
Day2T3没时间写, 改天写.
太长了不复制了, 看这里http://wikioi.com/problem/1197/
简单的字符串替换问题.
#include <stdio.h> #include <string.h> char k[102], str[1002]; int main() { scanf("%s%s", k, str); for (int i = 0, len = strlen(k); i < len; ++i) if (k[i] >= 'A' && k[i] <= 'Z') k[i] = 'a' + (k[i] - 'A'); for (int i = 0, p = 0, len = strlen(str), lenk = strlen(k); i < len; ++i, ++p, p = (p == lenk ? 0 : p)) if (str[i] >= 'A' && str[i] <= 'Z') { str[i] = str[i] - (k[p] - 'a'); if (str[i] - 'A' < 0) str[i] += 26; } else { str[i] = str[i] - (k[p] - 'a'); if (str[i] - 'a' < 0) str[i] += 26; } printf("%s\n", str); return 0; }
Day1T2
题目:http://wikioi.com/problem/1198/
给出一些二元组(Ai, Bi), 将他们按某种顺序排列. 对于每个二元组, 有一个值Fi, 计算方式是他之前所有二元组的Ai乘起来, 除以该二元组的Bi. 使得Max{F1, F2, F3 ... Fn}最小.
考虑交换相邻两个二元组的影响, 交换二元组i和二元组i+1,实际上只影响到Fi和Fi+1。
设T = A1 * A2 * ... * Ai-2 * Ai-1
方案A: Fi = T / Bi, Fi+1 =T * Ai / Bi+1
方案B: Fi = T / Bi+1, Fi+1 =T * Ai+1 / Bi
①假设1 / Bi < Ai / Bi+1, 1 / Bi+1 < Ai+1 / Bi
那么方案A优于方案B(Ai / Bi+1 < Ai+1 / Bi, Ai * Bi < Ai+1 * B i+1)
②假设1 / Bi ≥ Ai / Bi+1(Ai * Bi ≤ Bi+1)
那么1 / Bi ≤ Ai+1 / Bi, 方案A更优
③假设1 / Bi+1 ≥ Ai+1 / Bi(Ai+1 * Bi+1 ≤ Bi)
那么1 / Bi+1 ≤ Ai / Bi+1, 方案B更优
也就是说, 对于相邻两个二元组, 将A * B的较小的放在前面总能使答案更优.
实现要使用高精度, 结束.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXN 1001 struct Person { int a, b; } p[MAXN]; struct High { int len; int num[4001]; }; int n; bool operator < (Person n1, Person n2) { return n1.a * n1.b < n2.a * n2.b; } bool operator > (High n1, High n2) { if (n1.len > n2.len) return true; if (n1.len < n2.len) return false; for (int i = n1.len - 1; i > -1; --i) { if (n1.num[i] > n2.num[i]) return true; if (n1.num[i] < n2.num[i]) return false; } return true; } High operator * (High n1, int number) { int k = 0; n1.len += 5; for (int i = 0; i < n1.len; ++i) n1.num[i] *= number; for (int i = 0; i < n1.len; ++i) { n1.num[i] += k; k = n1.num[i]/10; n1.num[i] %= 10; } while (n1.num[n1.len - 1] == 0) --n1.len; return n1; } High operator / (High n1, int number) { High ans; memset(ans.num, 0, sizeof ans.num); ans.len = 0; int now = 0; for (int i = n1.len - 1; i > -1; --i) { now = now * 10 + n1.num[i]; if (now < number && ans.len == 0) continue; ans.num[ans.len++] = now / number; now %= number; } for (int i = 0; i < ans.len / 2; ++i) std::swap (ans.num[i], ans.num[ans.len - i - 1]); return ans; } void Give(High *n1, int number) { memset (n1 -> num, 0, sizeof n1->num); n1 -> len = 0; do { n1 -> num[(n1 -> len)++] = number % 10; number /= 10; } while (number != 0); } void Print(High n1) { for (int i = n1.len - 1; i > -1; --i) printf("%d", n1.num[i]); printf("\n"); } void Init() { scanf("%d", &n); for (int i = 0; i <= n; ++i) scanf("%d %d", &p[i].a, &p[i].b); for (int i = 1, j; i < n; ++i) for (j = i + 1; j <= n; ++j) if (!(p[i] < p[j])) std::swap (p[i], p[j]); } int main() { Init(); High s, maxx; Give(&s,p[0].a); Give(&maxx,0); for (int i = 1; i <= n; ++i) { if (s / p[i].b > maxx) maxx = s / p[i].b; s = s * p[i].a; } Print(maxx); return 0; }
Day1T3开车旅行
题目:http://wikioi.com/problem/1199/
对每个i点维护nextA[i], nextB[i]表示i往后A的话去哪里, B的话去哪里. 这个可以用链表来计算, 我们把所有数从小到大排序, 然后串成一个链表. 从1到n每次找Hi在链表里的前后4个判断一下, 然后把Hi从链表里删掉. 然后这样我们再用nextAB[i]表示i往后A走一次B走一次到哪里. 那么可以发现i->nextAB[i]形成了一颗树. 之后可以用树的算法做. 不过最简单的还是预处理next[i][j]表示i往后A和B都走了2^j次到哪里. 然后从大到小判2的每个次幂.
写的我太难受了, 连平时喜欢纠结的格式都没纠结成.
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <set> using namespace std; #define N 101000 #define L 14 #define INF 0x3f3f3f3f const double EPS = 1e-8; struct City { int h, id; inline City(): h(0), id(0) { } inline City(int h, int id): h(h), id(id) { } inline bool operator ==(const City &p) const { return (p.id == id); } }nexB , nexA ; struct Dist { int a, b, dis, id; inline Dist(): a(0), b(0), dis(INF), id(0) { } inline Dist(int a, int b, int dis, int id): a(a), b(b), dis(dis), id(id) { } inline Dist operator +(const Dist &p) const { return Dist(a + p.a, b + p.b, dis + p.dis, p.id); } }f [L], c; inline bool cmp1(const City &a, const City &b) {return a.h < b.h;} inline bool cmp2(const City &a, const City &b) {return a.h > b.h;} bool (*cp1)(const City &, const City &) = cmp1, (*cp2)(const City &, const City &) = cmp2; set <City, bool(*)(const City &, const City &)> lr(cp1), hr(cp2); int n, m, h ; inline void Update(int &dis, int dis1, City &CC, set <City>::iterator iter) { if ((dis > dis1) || (dis == dis1 && CC.h > (iter -> h))) dis = dis1, CC = *(iter); } inline City nex_B(const City &now, set<City>::iterator liter, set<City>::iterator hiter) { City CC; int dis = INF; int co = 1; for (set<City>::iterator iter = liter; co <= 1 && iter != lr.end(); iter ++, co ++) Update(dis, abs(now.h - (iter -> h)), CC, iter); co = 1; for (set<City>::iterator iter = hiter; co <= 1 && iter != hr.end(); iter ++, co ++) Update(dis, abs(now.h - (iter -> h)), CC, iter); if (dis == INF) return City(); return CC; } inline City nex_A(const City &now, set<City>::iterator liter, set<City>::iterator hiter) { City fir = nex_B(now, liter, hiter); City CC; int dis = INF; int co = 1; for (set<City>::iterator iter = liter; co <= 2 && iter != lr.end(); iter ++, co ++) if (!((*iter) == fir)) Update(dis, abs(now.h - (iter -> h)), CC, iter); co = 1; for (set<City>::iterator iter = hiter; co <= 2 && iter != hr.end(); iter ++, co ++) if (!((*iter) == fir)) Update(dis, abs(now.h - (iter -> h)), CC, iter); if (dis == INF) return City(); return CC; } void init() { scanf("%d", &n); for (int i = 1; i <= n; i ++) scanf("%d", &h[i]); for (int i = n; i >= 1; i --) { lr.insert(City(h[i], i)), hr.insert(City(h[i], i)); set<City>::iterator liter = ++ lr.lower_bound(City(h[i], i)), hiter = ++ hr.lower_bound(City(h[i], i)); City j = nex_A(City(h[i], i), liter, hiter); nexA[i] = j; nexB[i] = nex_B(City(h[i], i), liter, hiter); if (j == City()) continue ; int a = abs(h[i] - j.h), hj = j.h, b; j = nexB[j.id]; if (j == City()) continue ; b = abs(hj - j.h); f[i][0] = Dist(a, b, a + b, j.id); for (int j = 1; j < L; j ++) if (f[f[i][j - 1].id][j - 1].dis != INF) f[i][j] = f[i][j - 1] + f[f[i][j - 1].id][j - 1]; } } inline Dist go(int s, int x) { Dist CC = Dist(0, 0, 0, s); for (int i = L - 1; i >= 0; i --) if ((CC + f[CC.id][i]).dis <= x) CC = CC + f[CC.id][i]; City nex = nexA[CC.id]; if ((!(nex == City())) && (CC.dis + abs(h[CC.id] - nex.h) <= x)) CC = CC + Dist(abs(h[CC.id] - nex.h), 0, abs(h[CC.id] - nex.h), nex.id); return CC; } void solve() { double CC = (double) INF; int id = 0, x0, s, x; bool flag = false; scanf("%d", &x0); for (int i = 1; i <= n; i ++) { c = go(i, x0); if (c.b != 0 && ((double) c.a / (double)c.b < CC || (fabs((double) c.a / (double) c.b == CC)) && h[i] > h[id])) CC = (double) c.a / (double) c.b, id = i, flag = true; } if (!flag) cout << hr.begin() -> id << endl; else cout << id << endl; scanf("%d", &m); for (int i = 1; i <= m; i ++) scanf("%d%d", &s, &x), c = go(s, x), printf("%d %d\n", c.a, c.b); } int main() { init(); solve(); return 0; }
Day2T1同余方程
求关于 x 同余方程 ax ≡ 1 (mod b)的最小正整数解.
一句话: 用exgcd或则利用结论a^(phi(b)-1)%b.
#include <stdio.h> typedef long long int64; int64 stack_k[8193]; int top = -1; int64 extended_euclidian_algo(int64 x, int64 y) { int64 a = x, b = y, p, q; while (b != 0ll) { stack_k[++top] = (a / b) % y; int64 t = a % b; a = b; b = t; } p = 1ll; q = 0ll; while (top != -1) { int64 t = (p - q * stack_k[top--] % y) % y; if (t < 0) t += y; p = q; q = t; } return p; } int main() { int64 a, b; scanf("%I64d %I64d", &a, &b); scanf("%I64d\n", extended_euclidian_algo(a, b)); return 0; }
Day2T2借教室
题目:http://wikioi.com/problem/1217/
标准解法是:让我们考虑现在有m个操作,我们先把这些操作的端点离散化一下,之后可以注意到,相邻的两个关键点之间,只有最少的哪天是有用的,那么我们可以把天数压到2m级别。然后有m个操作,我们先加入前m/2个,然后用部分和判断一下前m/2个会不会跪,会的话就在前m/2个中递归,不然就在后m/2个中递归。由于我们每次可以压天数。复杂度函数就是F(n)=n+F(n/2)=O(n).
然后我很纠结的写了个二分...
#include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 1004000 int a , sum , n, m, d ; struct query { int x, y, d; }q ; inline int Read() { char ch = getchar(); while (!(ch >= '0' && ch <= '9')) ch = getchar(); int x = 0; while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x; } void init() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i ++) a[i] = Read(); for (int i = 1; i <= m; i ++) q[i].d = Read(), q[i].x = Read(), q[i].y = Read();//scanf("%d%d%d", &q[i].d, &q[i].x, &q[i].y); } bool check(int m) { memset(d, 0, sizeof(d)); for (int i = 1; i <= m; i ++) d[q[i].x] += q[i].d, d[q[i].y + 1] -= q[i].d; for (int i = 1; i <= n; i ++) { sum[i] = sum[i - 1] + d[i]; if (sum[i] > a[i]) return false; } return true; } int find(int l, int r) { int mid = (l + r) >> 1; while (l <= r) { if (check(mid)) l = mid + 1; else r = mid - 1; mid = (l + r) >> 1; } return r; } int main() { init(); int CC = find(0, m); if (CC == m) {puts("0\n");return 0;} printf("%d\n%d\n", -1, CC + 1); return 0; }
Day2T3没时间写, 改天写.
相关文章推荐
- NOIP2012提高组 开车旅行 解题报告
- NOIP2012 提高组复赛解题报告
- NOIP2012普及组解题报告
- 【noip2013】【提高组】【Day2】【解题报告】
- NOIP2016提高组复赛 解题报告
- Noip2016 提高d2 蚯蚓 解题报告
- 【NOIP2013】【提高组】【Day1】【解题报告】
- NOIP2010提高组复赛 解题报告(C/C++)(机械翻译)(乌龟棋)(关押罪犯)(引水入城)
- NOIp2015提高组 解题报告
- [NOIP 2012]解题报告
- NOIP2012普及组解题报告
- 2016.7.12 NOIP2013提高组day1解题报告(未完成版)
- NOIP2013 提高组复赛解题报告
- noip2011提高组day1+day2解题报告
- 2016.7.12 NOIP2013提高组 day2解题报告(未完成版)
- 【算法】NOIP2010提高组解题报告
- NOIP2014提高组复赛解题报告
- LuoguP1083 借教室[NOIP2012] 解题报告【二分答案+差分】
- NOIP2013提高组Day2 华容道 解题报告
- 2016 NOIP提高组复赛解题报告 C++