DOJ Round #1 解题报告
来源:互联网 发布:php数据库教程 编辑:程序博客网 时间:2024/05/17 00:18
欧姆的车
问题描述
小车正穿行在落基山脉蜿蜒曲折的盘山公路上,乔治·西蒙·欧姆正静静地望着窗外。
此时他正被伽伐尼电路引来的讦难弄的疲惫不堪,希望能借一次出行来舒缓情绪。
你坐在他的旁边,静静地望着他,感受着天才所遭受的不公对待。
正在这时,对面小孩正在摆弄的东西引起了欧姆的注意。是一块块电阻,几乎都没有什么差别。他时而摆摆弄弄,时而写写算算,看得出来他在很认真的思考。
欧姆不禁来了兴趣,询问了起来。“我是在用这几个相同的电阻,阻值当成1 ,尝试着串并联出一个阻值为2611 的电阻。。。不过好像有些困难呢”
欧姆沉吟片刻,对孩子说道:“8 个电阻便足以摆出来了。最少只要8 个。”
孩子眼睛一亮:“好厉害啊!那我随便出一个分数,你能算出来吗?”
欧姆此刻心烦意乱,哪有心思去想这个?于是就把这个简单的问题扔给了你。
意即对于一个分数,你需要找到最少的1 阻值的电阻数,使得可以串并联出一个阻值恰为分数值的大电阻。
你埋头苦干,而欧姆自己仍旧去欣赏窗外的艳媚骄阳。
然而他发现,自己似乎无法继续欣赏了,因为总有些东西照着他眼睛睁不开。
他循光看去,原来对面的孩子又拿出了一个三棱镜玩弄了起来。
他再也没办法忍受了,一把夺过了三棱镜,趁到站的功夫,下了车,朝空中使尽全力地扔了过去。。。
远处闪出了一丝电光。。。也许是要下雨了?
“可以,这很欧姆——水题一过,暴风雨就要来了。”
输入格式
输入仅一行,两个非负整数a(0≤a<260),b(0<b<260) ,其中a,b 分别表示分数的分子和分母,中间用空格隔开
输出格式
输出仅一个数,即最少使用的1 阻值的电阻个数
样例输入
26 11
样例输出
8
数据范围和提示
样例解释:8 个电阻的方案如右:3 个电阻并联,然后和1 个电阻串联,再依次和2 个电阻并联,最后和2 个电阻串联,经计算阻值为2611 ,且7 个电阻不能达到此目的
数据范围:
对于5% 的数据满足a=0
另有5% 的数据满足a=1,a≠b
另有5% 的数据满足b=1,a≠b
另有5% 的数据满足a=b
另有30% 的数据满足a≤10,b≤10
对于100% 的数据满足0≤a<260,0<b<260
温馨提示:此题可能用到 long long 类型,请使用 %lld 占位符输入输出或使用 cin,cout
看上去是一道物理题,然而是一道规律/脑洞题。
考场上并没有想出来什么好的做法,于是写了个50分的暴力递推。递推的方式是每次加一个电阻,枚举并联和串联两种方式。
正解如下。
考虑一个连分数
可以表示
可以证明,这种构造方式一定是符合要求的方案中所用电阻最少的。
我们记获取阻值为
容易得到如下递归式:
显然该算法与欧几里得算法的渐进复杂度相同,为
#include <iostream>using namespace std;typedef unsigned long long ULL;ULL a, b;ULL gcd(ULL x, ULL y) { return y == 0 ? 0 : x / y + gcd(y, x % y);}int main() { cin >> a >> b; cout << gcd(a, b) << endl; return 0;}
牛顿的树
问题描述
牛顿潇洒美少年,举觞白眼望青天,皎如玉树临风前~~
像往常一样地,艾萨克爵士正站在他家的树前思考人生,突然,一个三棱镜(砸到了他的脑袋上,牛顿,卒,享年23岁)从他面前飞过,牛顿的眼中闪过了奇异的光芒(神说:要有光),他默默地捡起了三棱镜,只听biu地一声,一条光谱射了出来(光谱上的巴尔末,帕邢,莱曼等人表示不知所措),牛顿觉得树上的某些枝干不好看,于是他决定通过架设很多三棱镜来遮盖树上的一些边,但是他很懒,所以他决定先思考一下再来架设。
首先给出树的节点数n ,牛顿的树与他现在的智商成正比,所以n≤300000
接下来n−1 行,每行两个整数u,v ,表示树上的一条边
由于你不可能既知道牛顿的智商,又通晓他的心思(牛顿不确定性原理),所以这是一棵无根树(废话)。
于是有一个整数m ,表示牛顿的思考次数,他最多可以在1 秒钟内思考300000 次
以下m 行数据格式如下
首先由一个数k 表示类型
当k=1 时,有两个整数u,v ,牛顿通过架设一个三棱镜遮住树边u,v ,于是u,v 这条边从树上消失(保证这条边之前存在)。
当k=2 时,撤销上一次放置的三棱镜。
当k=3 时,牛顿想知道树上的两个节点u,v 是否连通。
而你,是R国的某只程序员,你国的尤里X已经成功地读取到了牛顿的思考过程,你需要复现k=3 的结果,以供你国进行分析
输入格式
输入文件的第一行为一个整数n ,表示树的节点数
接下来的n−1 行,每行整数u,v ,表示u,v 有一条连边
接下来一行有一个整数m ,表示操作与询问数
接下来的m 行,每行首先给出一个整数k
对于k=1 ,给出两个整数u,v ,表示架设三棱镜摧毁u,v 这条边,保证这条边之前存在
对于k=2 ,表示撤销上一次的三棱镜,并保证上一次的三棱镜存在
对于k=3 ,给出两个整数u,v ,询问u,v 两点是否连通
输出格式
对于每个k=3 的操作,若u,v 连通则输出 true,否则输出 false
样例输入
5
1 2
2 3
3 4
4 5
10
1 5 4
1 4 3
3 5 2
1 2 1
3 1 1
3 3 1
1 3 2
3 1 3
3 1 5
3 2 2
样例输出
false
true
false
false
false
true
数据范围和提示
对于20% 的数据,n≤1000,m≤1000
对于另外50% 的数据,k≠2
对于100% 的数据,n≤300000,m≤300000,1≤u,v≤n
裸数据结构题,大意是维护一个森林 / 一些不相交集集合 的 连通性 / 从属关系。
对于一半的数据,只有拆分操作,没有联通操作。我们可以将操作倒序,于是就变成了一道轻松愉快的并查集水题。
对于全部数据,如果看作森林处理,可以写LCT或者树链剖分。然而,这道题具有一定的特殊性,即联通操作的对象一定是最近一次拆分操作的对象,针对这个性质,我们也可以写可持久化并查集。
这里上LCT代码。复杂度均摊
#include <stack>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 300030;int n, m, fa[N], child[N][2], mark[N];#define L(x) (child[x][0])#define R(x) (child[x][1])#define kd(x) (R(fa[x]) == x)#define isRoot(x) (L(fa[x]) != x && R(fa[x]) != x)#define setChild(f, c, t) (fa[child[f][t] = c] = f)inline void push(int x) { if (mark[x]) { mark[x] = 0; mark[L(x)] ^= 1; mark[R(x)] ^= 1; swap(L(x), R(x)); }}stack<int> ss;void pushDown(int x) { ss.push(x); while (! isRoot(x)) { ss.push(x = fa[x]); } while (! ss.empty()) { push(ss.top()); ss.pop(); }}//这里本来写的是递归形式//然而本地测试爆栈了(也许是我自己本地栈空间不够大)//然而递归交到doj上并没有爆栈inline void rotate(int x) { int y = fa[x], t = kd(x); setChild(y, child[x][t ^ 1], t); if (isRoot(y)) fa[x] = fa[y]; else setChild(fa[y], x, kd(y)); setChild(x, y, t ^ 1);}inline void splay(int x) { pushDown(x); while (! isRoot(x)) { if (! isRoot(fa[x])) { if (kd(x) == kd(fa[x])) rotate(fa[x]); else rotate(x); } rotate(x); }}inline void access(int x) { int tmp = 0; while (x) { splay(x); R(x) = tmp; tmp = x; x = fa[x]; }}inline void makeRoot(int x) { access(x); splay(x); mark[x] ^= 1;}inline void link(int u, int v) { makeRoot(u); fa[u] = v;}inline void cut(int u, int v) { makeRoot(u); access(v); splay(v); L(v) = fa[u] = 0;}inline int findRoot(int x) { access(x); splay(x); int tmp = x; while (L(tmp)) tmp = L(tmp); return tmp;}typedef pair<int, int> Pi;stack<Pi> S;int main() { cin >> n; int k, u, v; for (int i = 1; i < n; i++) { scanf("%d%d", &u, &v); link(u, v); } cin >> m; while (m--) { scanf("%d", &k); if (k == 2) { link(S.top().first, S.top().second); S.pop(); } else { scanf("%d%d", &u, &v); if (k == 1) { cut(u, v); S.push(make_pair(u, v)); } else { puts(findRoot(u) == findRoot(v) ? "true" : "false"); } } } return 0;}
最短的子区间(学长的题)
问题描述
(不要问我“最短”是谁
给你一个关键字集合
(key1,key2,...,keym)
和一个单词序列
(word1,word2,...,wordn)
请在这个单词序列中找到最短的子序列能够包含所有的关键字 (可以忽略关键字顺序)
序列最短即这个序列中包含的单词数最少
输入格式
有多组数据,每组数据占三行
对于每组数据:
第一行包含两个整数n,m , 分别代表关键字集合的大小和单词序列的长度
第二行包含m 个用空格隔开的关键字
第三行包含n 个用空格隔开的单词
输入数据以EOF 作为结束标志
注意: 每个关键字和单词的长度都小于100 , 关键字保证互不相同
输出格式
对于第 X 组测试数据:
如果存在这样的最短子序列, 则输出类似于 Case X: XX
如果不存在则只需要输出 Case X: -1
X 是从1 开始的整数, XX 是最短子序列的长度
样例输入
5 2
hello world
hell everyone this is good
10 3
sun apple tree
what is the sun tell apple tree sun call apple
样例输出
Case 1: -1
Case 2: 3
数据范围和提示
对于所有数据:1≤n≤105,1≤m≤100
首先,字符串匹配是必不可少的,对于该题这种多模板字符串匹配,我们可以考虑AC自动机(其实可以当作一般的Trie),当然也可以用hash。
匹配结束后,我么可以得到单词序列中每个单词所匹配的关键字序号。然后使用尺取法,维护一个始终向右移动的区间,不断更新包含所有关键字的子序列的最短长度。
总复杂度平均情况下
(起初我在hash中使用19260817这个模数,由于模数太大,初始化时间过长,且浪费空间,TLE,【暴力膜不可取】)
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int N = 100010, M = 111;const int mod = 19260817 / 9;struct node{ int pos, next;}lib[M];int n, m, mapPre[mod], nodeCnt;char s1[M][M], tmp[M];inline void add(int val, int pos) { lib[nodeCnt] = {pos, mapPre[val]}; mapPre[val] = nodeCnt++;}inline void initHashMap() { nodeCnt = 0; memset(mapPre, 0xff, sizeof(mapPre));}inline int getHash(const char s[]) { int val = 0; for (int i = 0; i < strlen(s); i++) { val *= 26; val %= mod; val += s[i] - 'a'; val %= mod; } return val;}inline void insert(const char s[], int pos) { int hashVal = getHash(s); add(hashVal, pos);}int cover[N];inline int init() { if (! ~ scanf("%d%d", &n, &m)) return 0; initHashMap(); memset(cover, 0xff, sizeof(cover)); for (int i = 1; i <= m; i++) { scanf("%s", s1[i]); insert(s1[i], i); } for (int i = 1; i <= n; i++) { scanf("%s", tmp); int val = getHash(tmp); if (mapPre[val] != -1) for (int j = mapPre[val]; j != -1; j = lib[j].next) { if (strcmp(s1[lib[j].pos], tmp) == 0) { cover[i] = lib[j].pos; break; } } } return 1;}int book[M];int coverCnt;inline void mainWork() { int ans = n + 1; memset(book, 0, sizeof(book)); coverCnt = 0; for (int i = 1, j = 1; i <= n; ) { if (coverCnt < m && j > n) break; if (coverCnt < m && j <= n) { if (cover[j] > 0) { book[cover[j]]++; if (book[cover[j]] == 1) coverCnt++; } j++; } else { if (coverCnt == m) ans = min(ans, j - i); if (cover[i] > 0) { book[cover[i]]--; if (book[cover[i]] == 0) coverCnt--; } i++; } } cout << ((ans == n + 1) ? -1 : ans) << endl;}//这里写得太丑,然而懒得修改了int cs;int main() { while (init()) { printf("Case %d: ", ++cs); mainWork(); } return 0;}
- DOJ Round #1 解题报告
- SDOI2016 Round 1解题报告
- BestCoder Round #1 解题报告
- Codeforces Round #250 (Div.1) 解题报告
- Codeforces Round #327 (Div. 1) 解题报告
- Codeforces Round #326 (Div. 1) 解题报告
- Codeforces Round #325 (Div. 1) 解题报告
- Round #374解题报告
- #Round 375解题报告
- CodeForces Round#35解题报告
- PKU Round 3解题报告
- Codeforces Round #277.5 解题报告
- BestCoder Round #55 解题报告
- Codeforces-Round #340 解题报告
- 【解题报告】BestCoder Round #75
- codeforces round 207 解题报告
- codeforces round 209 解题报告
- codeforces round 205 解题报告
- 10min了解ContentProvider
- C语言入门与进阶书籍汇总
- 遗传算法改进
- Java 线程池 四种创建方式
- oracle 12C数据库PSU补丁安装方式的变化---新增datapatch
- DOJ Round #1 解题报告
- 学习安装单机版Hadoop记录
- centos停止运行中的wget操作
- oracle 12C RAC启动实例时报ORA-00206: error in writing (block 1, # blocks 1) of control file错误处理
- sessionFactory工厂
- 在Windows7上通过Dao安装Tensorflow-(综合已有资料和自己调试经验的总结)
- Java EE微信开发入门一+环境搭配(JDK+MyEclipse+Tomcat)
- Java HashMap实现原理2——HashMap详解
- Netty ChannelOption 参数含义