Tutorials for 2014 SWJTU ACM Summer Training Team-PK Contest #1
来源:互联网 发布:江苏移动网络电视伴侣 编辑:程序博客网 时间:2024/05/22 01:25
A题:分段筛法+二分搜索。首先预处理出区间[1, 10^8]中的所有素数,然后对于查询区间[a, b],只需要用区间[1, b]中的素数个数减去区间[1, a - 1]中的素数个数,显然可以二分得到。在筛选素数的过程中,并不是直接筛,而是采用分段筛法,即将区间[1, 10^8]划分为50个不相交的等长区间,分别筛选即可。
参考代码:
//#define SPJ#ifdef SPJ#include <bits/stdc++.h>#else#include <algorithm>#include <bitset>#include <cassert>#include <cmath>#include <complex>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <iostream>#include <list>#include <map>#include <numeric>#include <queue>#include <set>#include <sstream>#include <stack>#include <string>#include <vector>using namespace std;#endif#define dbg(x) cout << #x << " = " << x << endl#define dbg2(x,y) cout << #x << " = " << x << ", " << #y << " = " << y << endl#define dbg3(x,y,z) cout << #x << " = " << x << ", " << #y << " = " << y << ", " << #z << " = " << z << endl#define out(x) cout << (x) << endl#define out2(x,y) cout << (x) << " " << (y) << endl int prime[6000000]; int tot_prime = 0;bool vst[2000000 + 10];void Get_prime_1(int M){/* memset(vst, 0, sizeof(vst)); int i, j, k, Sq = 1 + (int)sqrt((M << 1) + 1.0); for(i = 3; i <= Sq; i += 2) { if( vst[i >> 1] ) continue; for(j = i * i, k = (i << 1); j <= ((M << 1) | 1); j += k) vst[j >> 1] = 1; } for(i = 1; i <= M; i ++) if( !vst[i] ) prime[tot_prime ++] = (i << 1) | 1;*/ int i, j; memset(vst, 0, sizeof(vst)); for(i = 3; i <= M; i += 2) { if( vst[i] ) continue; prime[tot_prime ++] = i; for(j = i << 1; j <= M; j += i) vst[j] = 1; }}void Get_prime_2(int l, int r){/* memset(vst, 0, sizeof(vst)); int i, j, k; for(i = 1; prime[i] * prime[i] <= ((r << 1) | 1); i ++) { j = ((l << 1) | 1) / prime[i]; if( j % 2 == 0 ) j ++; j = j * prime[i]; for(k = prime[i] << 1; j <= ((r << 1) | 1); j += k) vst[(j >> 1) - l] = 1; } for(i = l; i <= r; i ++) { if( !vst[i - l] && tot_prime < 5900000 ) prime[tot_prime ++] = ((i << 1) | 1); }*/ memset(vst, 0, sizeof(vst)); int i, j; for(i = 0; prime[i] * prime[i] <= r; i ++) { j = l / prime[i] * prime[i]; if( j < l ) j += prime[i]; for(; j <= r; j += prime[i]) vst[j - l] = 1; } for(i = l; i <= r; i ++) { if( !vst[i - l] ) { prime[tot_prime ++] = i; } }}void init(){ tot_prime = 0; prime[tot_prime ++] = 2; Get_prime_1(2000000); //printf("1: tot_prime = %d\n", tot_prime); for(int i = 2; i <= 50; i ++) { Get_prime_2(2000000 * (i - 1) + 1, 2000000 * i); //printf("tot_prime = %d\n", tot_prime); } //dbg(tot_prime);}int main(){ //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); init(); int N, a, b, l, r, m, pos, pos_2; while( scanf("%d %d", &a, &b) == 2 ) { N = b; l = 0, r = tot_prime - 1, pos = -1; while( l <= r ) { m = (l + r) >> 1; if( prime[m] <= N ) pos = m, l = m + 1; else r = m - 1; } N = a - 1; l = 0, r = tot_prime - 1, pos_2 = -1; while( l <= r ) { m = (l + r) >> 1; if( prime[m] <= N ) pos_2 = m, l = m + 1; else r = m - 1; } printf("%d\n", (pos + 1) - (pos_2 + 1)); } return 0;}
B题:线段树+动态规划。容易推导出动态规划递推表达式,即DP[i] = max{DP[j] + A[i]}, (A[j] < A[i], 1 <= j < i)。但是这样做复杂度相当高。此时,我们需要优化,我们可以先对数组的值进行非递减排序,相同的值按照索引的大小排序(这一点很有必要)。然后,建立以索引为区间的线段树,进行动态更新和查询。
参考代码:
//#define SPJ#ifdef SPJ#include <bits/stdc++.h>#else#include <algorithm>#include <bitset>#include <cassert>#include <cmath>#include <complex>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <iostream>#include <list>#include <map>#include <numeric>#include <queue>#include <set>#include <sstream>#include <stack>#include <string>#include <vector>using namespace std;#endif#define dbg(x) cout << #x << " = " << x << endl#define dbg2(x,y) cout << #x << " = " << x << ", " << #y << " = " << y << endl#define dbg3(x,y,z) cout << #x << " = " << x << ", " << #y << " = " << y << ", " << #z << " = " << z << endl#define out(x) cout << (x) << endl#define out2(x,y) cout << (x) << " " << (y) << endl const int maxN = 100000 + 2;typedef long long i64d;struct Seq{ int idx; i64d val; inline void in(int &_idx) { scanf("%I64d", &val); idx = _idx; } inline bool operator<(const Seq &s) const { if( val != s.val ) return val < s.val; return idx > s.idx; }};Seq A[maxN]; int nA;struct node{ int l, r; i64d val;};node tree[maxN * 5];void build(int l, int r, int idx){ tree[idx].l = l; tree[idx].r = r; tree[idx].val = 0LL; if( l == r ) return ; int Mid = (l + r) >> 0x1; build(l, Mid, idx << 0x1); build(1 + Mid, r, (idx << 0x1) + 1);}void update(int idx, int &ip, i64d &val){ if( tree[idx].l == tree[idx].r ) { tree[idx].val = val; return ; } int Mid = (tree[idx].l + tree[idx].r) >> 0x1; if( ip <= Mid ) update(idx << 0x1, ip, val); else update(1 + (idx << 0x1), ip, val); tree[idx].val = max(tree[idx << 0x1].val, tree[(idx << 0x1) + 1].val);}i64d query(int l, int r, int idx){ if( l == tree[idx].l && tree[idx].r == r ) return tree[idx].val; int Mid = (tree[idx].l + tree[idx].r) >> 0x1; if( r <= Mid ) return query(l, r, idx << 0x1); else if( Mid < l ) return query(l, r, (idx << 0x1) + 1); else return max(query(l, Mid, idx << 0x1), query(1 + Mid, r, (idx << 0x1) + 1));}i64d dp[maxN];int main(){ //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); while( scanf("%d", &nA) == 1 ) { for(int i = 0; i < nA; ++i) A[i].in(i); sort(A, A + nA); build(0, nA - 1, 1); i64d tmp; for(int i = 0; i < nA; ++i) { tmp = query(0, A[i].idx, 1); //printf("tmp = %lld, idx = %d\n", tmp, A[i].idx); dp[i] = A[i].val + tmp; update(1, A[i].idx, dp[i]); } i64d ans = dp[0]; for(int i = 1; i < nA; ++i) if( dp[i] > ans ) ans = dp[i]; printf("%I64d\n", ans); } return 0;}
C题:推导题。这题有同学预先对字符串排序,失策没有卡死。实际上只需要统计每个字符的频率,然后从a到z获取频率为奇数的字符,并把得到的字符串中的最后一个字符去掉,就是答案(如果是空串就输出0)。
//#define SPJ#ifdef SPJ#include <bits/stdc++.h>#else#include <algorithm>#include <bitset>#include <cassert>#include <cmath>#include <complex>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <iostream>#include <list>#include <map>#include <numeric>#include <queue>#include <set>#include <sstream>#include <stack>#include <string>#include <vector>using namespace std;#endif#define dbg(x) cout << #x << " = " << x << endl#define dbg2(x,y) cout << #x << " = " << x << ", " << #y << " = " << y << endl#define dbg3(x,y,z) cout << #x << " = " << x << ", " << #y << " = " << y << ", " << #z << " = " << z << endl#define out(x) cout << (x) << endl#define out2(x,y) cout << (x) << " " << (y) << endlconst int maxN = 1000000 + 10;char str[maxN];int cnt[26];int main(){ //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); int i, j; while( scanf("%s", str) == 1 ) { memset(cnt, 0, sizeof(cnt)); for(i = 0; str[i]; i ++) cnt[ str[i] - 'a' ] ++; for(i = 25; i >= 0; i --) if( cnt[i] & 1 ) { cnt[i] ++; break; } j = 0; for(i = 0; i < 26; i ++) if( cnt[i] & 1 ) { j = 1; putchar('a' + i); } if( j == 0 ) putchar('0'); putchar('\n'); } return 0;}
D题:推导题。有内存限制,意味着我们不能用数组存储,如果发现输入数据是64比特,n的规模又大,意味着我们往往需要使用getchar读入数据。接下来有两种思想,我实现了第2个,不知道有没有人用第1个,或者第1个会被卡掉?
第1种思想:容易在读入数据的过程中,得到3个数,即min(最小的数),max(最大的数),sum(和),显然答案等于 sum - (max - min + 1) * (max + min) / 2,小数组高精度处理即可。
第2种思想:同样的,我们可以在输入的过程中得到3个数,即min(最小的数),max(最大的数),XOR(所有数的异或值),显然答案等于 XOR ^ [min ^ (min + 1) ^ ... ^ max],这里 ^ 表示异或运算。此时问题变成了需要求区间[min, max]所有的数的异或值,这是OJ上的一道陈题(链接),这里简单说下一种求解区间[a, b]所有的数的异或值的解法:
假设x是偶数,那么x ^ (x+1) 就等于1,也即x和(x+1)只有最低位不同。好了,我们会发现区间[a, b]是由大量的这样的x和x+1构成(除了边界情况)。
所以我们只需要从a和b的奇偶性入手即可,这里只考虑其中一种情况,即a是奇数,b是偶数,那么我们总可以把区间[a, b]划分为:
a, (a+1, a+2), (a+3, a+4), ..., (b-2, b - 1), b
显然中间有括号的两个数的异或值为1,假设这样的配对的数有y个,那么答案就是a ^ (y & 1) ^ b,不是吗?(其他情况可以参考着来。)
参考代码:
//#define SPJ#ifdef SPJ#include <bits/stdc++.h>#else#include <algorithm>#include <bitset>#include <cassert>#include <cmath>#include <complex>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <iostream>#include <list>#include <map>#include <numeric>#include <queue>#include <set>#include <sstream>#include <stack>#include <string>#include <vector>using namespace std;#endif#define dbg(x) cout << #x << " = " << x << endl#define dbg2(x,y) cout << #x << " = " << x << ", " << #y << " = " << y << endl#define dbg3(x,y,z) cout << #x << " = " << x << ", " << #y << " = " << y << ", " << #z << " = " << z << endl#define out(x) cout << (x) << endl#define out2(x,y) cout << (x) << " " << (y) << endlunsigned long long gao(unsigned long long l, unsigned long long r){ if( l == r ) return l; unsigned long long res = 0; if( l & 1LL ) { res ^= l; l ++; } if( !(r & 1LL) ) { res ^= r; r --; } if( l > r ) return res; unsigned long long last = ((r - l + 1LL) >> 1) & 1LL; return (res ^ last);}int main(){ //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); int n, i; unsigned long long mi, ma, res, ans; unsigned long long val; char ch; while( scanf("%d", &n) == 1 ) { //scanf("%llu", &val); while( (ch = getchar()) == ' ' || ch == '\n' ) ; val = ch - '0'; while( (ch = getchar()) >= '0' && ch <= '9' ) val = val * 10 + (ch - '0'); ans = mi = ma = val; for(i = 1; i < n; i ++) { //scanf("%llu", &val); while( (ch = getchar()) == ' ' || ch == '\n' ) ; val = ch - '0'; while( (ch = getchar()) >= '0' && ch <= '9' ) val = val * 10 + (ch - '0'); if( mi > val ) mi = val; if( ma < val ) ma = val; ans ^= val; } res = gao(mi, ma); //dbg3(mi, ma, res); printf("%llu\n", (res ^ ans)); } return 0;}
E题:卡平方题。首先数据量大,请用getchar;其次再去计算每个数出现的次数,此时你可能会用平方(即 k * 100 *100)的复杂度去统计数据,如果输入数据有10^5组小数据,就很危险,可以先预处理,对付10^5组小数据的复杂度消耗是10^8数量级。(下一次可能有一道变型的题,请关注此题。)
参考代码:
//#define SPJ#ifdef SPJ#include <bits/stdc++.h>#else#include <algorithm>#include <bitset>#include <cassert>#include <cmath>#include <complex>#include <cstdio>#include <cstdlib>#include <cstring>#include <ctime>#include <iostream>#include <list>#include <map>#include <numeric>#include <queue>#include <set>#include <sstream>#include <stack>#include <string>#include <vector>using namespace std;#endif#define dbg(x) cout << #x << " = " << x << endl#define dbg2(x,y) cout << #x << " = " << x << ", " << #y << " = " << y << endl#define dbg3(x,y,z) cout << #x << " = " << x << ", " << #y << " = " << y << ", " << #z << " = " << z << endl#define out(x) cout << (x) << endl#define out2(x,y) cout << (x) << " " << (y) << endlconst int maxN = 100 + 2;vector<int> adj[maxN];int sz[maxN];int gcd(int a, int b){ if( b == 0 ) return a; return gcd(b, a % b);}void init(){ for(int i = 100; i >= 1; i --) { for(int j = i; j >= 1; j --) if( gcd(i, j) == 1 ) { adj[i].push_back(j); } sz[i] = (int)adj[i].size(); }}int n, val, i, j, k;long long times[maxN];long long res, tmp;char ch;int main(){ //freopen("data.in", "r", stdin); //freopen("data.out", "w", stdout); init(); while( scanf("%d", &n) == 1 ) { memset(times, 0, sizeof(times)); k = 0; for(i = 0; i < n; i ++) { //scanf("%d", &val); while( (ch = getchar()) == ' ' || ch == '\n' ) ; val = ch - '0'; while( (ch = getchar()) >= '0' && ch <= '9' ) val = val * 10 + (ch - '0'); times[val] ++; if( k < val ) k = val; } res = times[1] * times[1]; for(i = k; i > 1; i --) { if( !times[i] ) continue; tmp = 0LL; times[i] <<= 1; for(j = 0; j < sz[i]; j ++) tmp += times[ adj[i][j] ]; res += times[i] * tmp; } printf("%lld\n", res); } return 0;}
(备注:所有比赛中都是解出0题最后的实习成绩并不一定就是不及格。还有其他成绩,比如100道题要求、出勤分等等,完成了集训的定量基本要求,肯定及格,不必太担心。)
- Tutorials for 2014 SWJTU ACM Summer Training Team-PK Contest #1
- Tutorials for 2014 SWJTU ACM Summer Training for Freshman
- The 3rd ACM-ICPC Summer Training Contest(swjtu)
- Final Transcripts of SWJTU ACM-ICPC Summer Training (2014)
- Tutorials for 2014 SWJTU Freshman Invitation Programming Contest - Online Round
- Summer Training Team Selection (1) Problem A ACM Contest Scoring 水题
- 2015 SWJTU ACM Summer Training Final Assessment 1st 部分题解
- 2016 UESTC ACM Summer Training Team Selection (2)解题报告
- Tutorials for The 10th SWJTU Collegiate Programming Contest - Qualification Round
- Tutorials for The 10th SWJTU Collegiate Programming Contest - Online Round
- UESTC Summer Team Training #2
- SDKD Summer Team Contest A
- SDKD Summer Team Contest B
- SDKD Summer Team Contest C
- SDKD Summer Team Contest D
- SDKD Summer Team Contest E
- SDKD Summer Team Contest F
- SDKD Summer Team Contest I
- Hadoop mapreduce 中partition作用
- 面试-单例模式
- 一个合格程序员该做的事情——你做好了吗?
- C++控制结构-条件语句
- [ios版本]AR 现实增强之高通Vuforia QCAR SDK (二)
- Tutorials for 2014 SWJTU ACM Summer Training Team-PK Contest #1
- 深入探索Java-Hashmap
- hdu 3591 The trouble of Xiaoqian 多重背包+完全背包
- C++控制结构简介
- 判断文件是否存在
- 从倒数第二个字符开始截取
- SVN安装配置与使用
- 签了工作之后才发现,自己太草率了.....我看过的关于职业规划最好最全面的一篇文章 分类: 体会
- POJ3261 Milk Patterns