某比赛的某些题目

这里我只写一些认为应该写报告的题目。。。像那种5分钟敲出代码的题就不写了。感谢@xianbin5组织这场比赛,Orz


题目1:给一些数据,建议一颗排序二叉树。然后找到某个节点的祖父节点(父节点的父节点)n <= 50000;

题解:描述很简单,而且可以很简单的敲出来。但是注意数据量。。。所以这里要用到特殊的东西。

一个颗很神奇的数:笛卡尔树

笛卡尔树跟treap结构完全一样。不过区别在于treap的heap部分是随机取的,笛卡尔树的val是之前确定好的。

笛卡尔树的构造过程:先对数据的key值进行从小到大排序,然后一次插入笛卡尔树。过程跟treap一样,不过可以发现,因为数据是排好序的,所以每次插入的节点肯定是在原树最右侧的节点开始找的。这样就能实现最优甚至接近O(n)的时间建树。。。。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>
#include <stack>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const int eps = 1e-6;
const int inf = ~0u>>2;;
typedef long long LL;

using namespace std;

const int N = 50010;

map<int, int> mp;

struct node {
    int key, val;
    int ls, rs, pre;
} p[N];

bool operator < (const node& a, const node& b) {
    return a.key < b.key;
}

void build_tree(int n) {
    int i, k = 1;
    p[0].rs  = 1;

    for(i = 2; i <= n; ++i) {
        while(p[k].val > p[i].val)  k = p[k].pre;
        p[i].ls = p[k].rs;
        p[p[k].rs].pre = i;
        p[k].rs = i;
        p[i].pre = k;
        k = i;
    }

}

int main() {
    //freopen("data.in", "r", stdin);

    int n, m, i, x;
    while(~scanf("%d%d", &n, &m)) {
        mp.clear();
        for(i = 0; i <= n; ++i) {
            p[i].pre = 0; p[i].val = 0;
            p[i].ls = p[i].rs = 0;
        }
        for(i = 1; i <= n; ++i) {
            scanf("%d", &p[i].key);
            p[i].val = i;
            p[i].pre = 0;
        }
        sort(p + 1, p + n + 1);
        for(i = 1; i <= n; ++i) {
            mp[p[i].key] = i;
        }
        build_tree(n);

        while(m--) {
            scanf("%d", &x);
            x = mp[x];
            if(p[x].pre == 0)  {puts("-1"); continue;}
            x = p[x].pre;
            if(p[x].pre == 0)  {puts("-1"); continue;}
            x = p[x].pre;
            printf("%d\n", p[x].key);
        }
    }
    return 0;
}

 

 

题目2:给出由'0','1','2','3','?'组成的串A,B,C。其中'?'表示该位可能是0,1,2,3,若有A+B=C,则称[A,B,C]是这三个字符串的一组解,给出A,B,C,求解的个数。

 题解:这题很容易想到模拟。不过我写的模拟没过。。。还有更好的方法。。。dp

dp[i][0]表示第i位没有进位的情况数。

dp[i][1]表示第i位有进位时的情况数。

 

然后对每一位进行模拟加法

View Code
string a, b, c;

int dp[20][2];

int solve() {
    CL(dp, 0);
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    reverse(c.begin(), c.end());

    int i, j, k, l, cy, m = 0;
    int sa, ea, sb, eb, sc, ec;

    dp[0][0] = 1; dp[0][1] = 0;

    for(i = 0; i < a.length() || i < b.length() || i < c.length(); ++i) {
        sa = sb = sc = 0; ++m;
        if(i < a.length())  ea = 3;
        else    ea = 0;
        if(i < b.length())  eb = 3;
        else    eb = 0;
        if(i < c.length())  ec = 3;
        else    ec = 0;

        if(a[i] != '?' && i < a.length()) sa = ea = a[i] - '0';
        if(b[i] != '?' && i < b.length()) sb = eb = b[i] - '0';
        if(c[i] != '?' && i < c.length()) sc = ec = c[i] - '0';

        for(j = sa; j <= ea; ++j) {
            for(k = sb; k <= eb; ++k) {
                for(l = sc; l <= ec; ++l) {
                    for(cy = 0; cy < 2; ++cy) {
                        if(l == (j + k + cy)%4) {
                            dp[i+1][(j + k + cy)/4] += dp[i][cy];
                        }
                    }
                }
            }
        }
    }
    return dp[m][0];
}

int main() {
    //freopen("data.in", "r", stdin);

    while(cin >> a >> b >> c) {
        int ans = solve();
        cout << ans << endl;
    }
    return 0;
}

 

题目3:给一个数n,找出[1, n]中包含因子个数最多的数,如果这个数有多个,找最小的那一个。

题解:对n分解质因子 n = p1^k1 * p2^k2 * ..... 所以n的因子数为 (k1 + 1)*(k2 + 1) *(k3 + 1) * ....

可以反过来,不去分解质因子,而是用这些素数去组合,枚举出1-n中所有数有多少个质因子

其实前边这些已经可以解决这个问题了,不过还有一个规律进行优化。

12 = 3 * 2^2 18 = 3^2 * 2;

12 < 18,可以在枚举时进行一个优化,使得枚举到的数字中2的指数不小于3的指数,3的指数不小于5的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。再对于每一个得到的数进行比较和记录。

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>
#include <stack>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   x < y ? x : y
#define Max(x, y)   x < y ? y : x
#define E(x)    (1 << (x))

const int eps = 1e-6;
const int inf = ~0u>>2;;
typedef long long LL;

using namespace std;

int prime[12] = {2,3,5,7,11,13,17,19,23,29,31,37};
int _max, num, n;

void solve(int m, int t, int f, int limt) {
    int j, l;
    LL  i, p;
    if(t > _max || (t == _max && m < num))    {num = m; _max = t;}
    j = 0; l = 1; i = m;
    while(i <= n && j < limt) {
        j++; l++; i = i*prime[f]; p = t*l;
        if(i <= n)  solve(i, p, f + 1, j);
    }
}

int main() {
    //freopen("data.in", "r", stdin);

    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        _max = 1; num = 1;
        if(n == 1)  puts("1");
        else {
            solve(1, 1, 0, 30);
            printf("%d\n", num);
        }
    }
    return 0;
}

 

 

 

 

 

 

 

 

 

 

 

posted @ 2012-07-22 07:31  AC_Von  阅读(189)  评论(2编辑  收藏  举报