Gemini Boy - ACM之路~
20130926 - 初级魔法练习
叉姐の魔法训练
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=32389#overview
A: Set Operatoin (POJ2443)
题意是:有n个集合(n<=1000), 每个集合有m个数ai(m<=10000, 1=<ai<=10000),同一个集合中可能有相同的数.有q个查询(q<=200000),对于每个查询a,b,问是否存在一个集合同时包含a和b。
做法就是压位加速,将每一个集合看成是一行,也成了一个1000*10000的0、1矩阵,对于每个数来书,它所在的列的0、1分布情况也就是它所在集合的情况。但问题是现在一共有1000行,2^1000肯定不行,但考虑到一个int可以存32位(2^32),1000<32*32,所以可以开32个整数,每个整数的二进制的每一位代表每一行,这样就可以在q*32的可接受的时间复杂度内查询出结果。将扫描1000变为扫描32,利用整数的位操作减少为1/32。
代码君:
package NO20130926; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.StringTokenizer; public class POJ2443 { public void run() { InputReader cin = new InputReader(System.in); PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out)); int[][] map = new int[10010][100]; int n = cin.nextInt(); for (int i = 0; i < n; i++) { int c = cin.nextInt(); for (int j = 0; j < c; j++) { int x = cin.nextInt(); map[x][i / 30] |= 1 << (i % 30); } } int q = cin.nextInt(); for (int cas = 0; cas < q; cas++) { int a = cin.nextInt(); int b = cin.nextInt(); boolean ok = false; for (int i = 0; i < 35; i++) { if ((map[a][i] & map[b][i]) > 0) { cout.println("Yes"); ok = true; break; } } if (ok == false) cout.println("No"); } cout.flush(); } public static void main(String args[]) { new POJ2443().run(); } class InputReader { public BufferedReader reader; public StringTokenizer tokenizer; public InputReader(InputStream stream) { reader = new BufferedReader(new InputStreamReader(stream)); tokenizer = null; } public InputReader() throws FileNotFoundException { reader = new BufferedReader(new FileReader("d:/input.txt")); tokenizer = null; } public String next() { while (tokenizer == null || !tokenizer.hasMoreTokens()) { try { tokenizer = new StringTokenizer(reader.readLine()); } catch (IOException e) { throw new RuntimeException(e); } } return tokenizer.nextToken(); } public int nextInt() { return Integer.parseInt(next()); } public long nextLong() { return Long.parseLong(next()); } } }
当然也可以用C++ STL的bitset或java的bitset直接来做。
浅谈莫比乌斯反演
莫比乌斯反演是应用在有限偏序集上的容斥原理,什么是有限偏序集:令X是一个集合,X上的关系R是一个“性质”,在X的任意俩个给定的元素之间,这个性质可能成立也可能不成立。说的更正式一点,X上的关系是X×X的一个子集R,而X×X是X的元素的序偶集合。常见的偏序有:有一个集合含于另一个集合以及由一个整数被另一个整数。它们是在给定俩个集合互相都不是对方自己以及给定俩个整数彼此都不能整除意义下的偏序。
莫比乌斯反演:
- f(x) = sigma{g(d)}其中x % d == 0,则g(x) = sigma{miu(d) * f(x/d)}
- f(x) = sigma{g(d)}其中d % x == 0,则g(x) = sigma{miu(d/x) * f(d)}
莫比乌斯反演中miu(x) =
- 1 {x中含有偶数个不同的质因子}
- -1 {x中含有奇数个不同的质因子}
- 0 {其他情况}
求miu(x)的代码:
const int maxn = 1001000; int prime[maxn], miu[maxn], cnt; bool check[maxn]; void Mobius() { memset(check, false, sizeof(check)); cnt = 0; miu[1] = 1; for (int i = 2; i < maxn; i++) { if (!check[i]) { prime[cnt++] = i; miu[i] = -1; } for (int j = 0; j < cnt; j++) { if (i * prime[j] > maxn) break; check[i*prime[j]] = true; if (i % prime[j]) miu[i*prime[j]] = 1; else { miu[i*prime[j]] == 0; break; } } } }
Codeforces 291E. Tree-String Problem
链接:http://codeforces.com/problemset/problem/291/E
题意就是说给你一棵树,边权是字符串,然后从根节点到叶子节点的所有路径中,出现模板串的个数。
做法就是dfs+kmp,对目标串得到next数组,然后从根节点dfs,一边dfs一边跑kmp进行匹配。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #include <string> using namespace std; const int maxn = 100100; struct Edge { int to; string s; Edge() {} Edge(int too, string str) { to = too; s = str; } }; int n, ans = 0, next[maxn]; string t; vector<Edge> edge[maxn]; void dfs(int u, int k) { for (int l = 0; l < edge[u].size(); l++) { int v = edge[u][l].to; string str = edge[u][l].s; int tmp = k, len = edge[u][l].s.size(); for (int i = 0, j = k; i < len; i++) { while (j != -1 && str[i] != t[j+1]) j = next[j]; if (str[i] == t[j+1]) j++; if (j + 1 == t.size()) { j = next[j]; ans++; } tmp = j; } dfs(v, tmp); } } void work() { cin >> n; for (int i = 2; i <= n; i++) { int p; string str; cin >> p >> str; edge[p].push_back(Edge(i, str)); } cin >> t; next[0] = -1; for (int i = 1, j = -1; i < t.size(); i++) { while (j != -1 && t[j+1] != t[i]) j = next[j]; if (t[j+1] == t[i]) { next[i] = j + 1; j++; } else { next[i] = -1; } } dfs(1, -1); cout << ans << endl; } int main() { work(); return 0; }
SGU 499. Greatest Greatest Common Divisor
链接:http://acm.sgu.ru/problem.php?contest=0&problem=499
给出n(1<=n<=100000)个数,每个数的范围是1<=a[i]<=1000000,现在求n个数任意俩个数的gcd最大是多少。
做法是枚举答案,然后用类似于素数筛法的方法判定是否存在。复杂度O(n*logn)。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> using namespace std; const int maxn = 1000010; int n, maxx, a[100100], pos[maxn]; void init() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); pos[a[i]] = i; maxx = max(a[i], maxx); } maxx += 2; } bool check(int x) { int res = 0; for (int i = x; i < maxx; i += x) { if (res > 1) break; if (pos[i]) res++; } if (res > 1) return true; else return false; } void work() { for (int i = maxx - 2; i >= 1; i--) { if (check(i) == true) { printf("%d\n", i); return ; } } } int main() { init(); work(); }
随想
还有47天就是南京现场赛了。凭心而论,压力很大,现在天天只能睡5,6个小时,就要爬起来坐到电脑前做题。当然这不能怪别的,只能怪自己在过去的一年多没有好好练,挖的坑太大,可能没有时间填坑了。但是既然努力了1年多了,就不想在这最后的几十天留下遗憾。
列一下ToDoList(自己之前搞的不错的东西就不列出来了):
1.动态规划
- 数位DP
- 树形DP
- 状态压缩
2.数据结构
- 树套树
- CDQ分治
- 树链剖分
- 动态树
3.字符串
- AC自动机
- 后缀数组
- hash
- 后缀自动机
4.图论
- 网络流
- 连通分量
5.数学
- 高斯消元
- 概率与期望
- 组合数学
- 线性方程
要做的一些比赛:
POI 15-19th
Andrew Stankevich Contest 1-10
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=31687#overview
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=31688#overview
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=31294#overview
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=31535#overview
...(未完待续)