这里我只写一些认为应该写报告的题目。。。像那种5分钟敲出代码的题就不写了。感谢@xianbin5组织这场比赛,Orz
题目1:给一些数据,建议一颗排序二叉树。然后找到某个节点的祖父节点(父节点的父节点)n <= 50000;
题解:描述很简单,而且可以很简单的敲出来。但是注意数据量。。。所以这里要用到特殊的东西。
一个颗很神奇的数:笛卡尔树
笛卡尔树跟treap结构完全一样。不过区别在于treap的heap部分是随机取的,笛卡尔树的val是之前确定好的。
笛卡尔树的构造过程:先对数据的key值进行从小到大排序,然后一次插入笛卡尔树。过程跟treap一样,不过可以发现,因为数据是排好序的,所以每次插入的节点肯定是在原树最右侧的节点开始找的。这样就能实现最优甚至接近O(n)的时间建树。。。。
#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位有进位时的情况数。
然后对每一位进行模拟加法
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的指数……这样我们就能够得到质因数分解“模式”相同的最小数(证明略)。再对于每一个得到的数进行比较和记录。
#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; }