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道题要求、出勤分等等,完成了集训的定量基本要求,肯定及格,不必太担心。)

0 0
原创粉丝点击