DOJ Round #1 解题报告

欧姆的车

问题描述
小车正穿行在落基山脉蜿蜒曲折的盘山公路上,乔治·西蒙·欧姆正静静地望着窗外。
此时他正被伽伐尼电路引来的讦难弄的疲惫不堪,希望能借一次出行来舒缓情绪。
你坐在他的旁边,静静地望着他,感受着天才所遭受的不公对待。
正在这时,对面小孩正在摆弄的东西引起了欧姆的注意。是一块块电阻,几乎都没有什么差别。他时而摆摆弄弄,时而写写算算,看得出来他在很认真的思考。
欧姆不禁来了兴趣,询问了起来。“我是在用这几个相同的电阻,阻值当成 1 1 1,尝试着串并联出一个阻值为 26 11 \frac{26}{11} 1126的电阻。。。不过好像有些困难呢”
欧姆沉吟片刻,对孩子说道:“ 8 8 8个电阻便足以摆出来了。最少只要 8 8 8个。”
孩子眼睛一亮:“好厉害啊!那我随便出一个分数,你能算出来吗?”
欧姆此刻心烦意乱,哪有心思去想这个?于是就把这个简单的问题扔给了你。
意即对于一个分数,你需要找到最少的 1 1 1阻值的电阻数,使得可以串并联出一个阻值恰为分数值的大电阻。
你埋头苦干,而欧姆自己仍旧去欣赏窗外的艳媚骄阳。
然而他发现,自己似乎无法继续欣赏了,因为总有些东西照着他眼睛睁不开。
他循光看去,原来对面的孩子又拿出了一个三棱镜玩弄了起来。
他再也没办法忍受了,一把夺过了三棱镜,趁到站的功夫,下了车,朝空中使尽全力地扔了过去。。。
远处闪出了一丝电光。。。也许是要下雨了?
“可以,这很欧姆——水题一过,暴风雨就要来了。”
输入格式
输入仅一行,两个非负整数 a ( 0 ≤ a &lt; 2 60 ) , b ( 0 &lt; b &lt; 2 60 ) a(0≤a&lt;2^{60}),b(0&lt;b&lt;2^{60}) a(0a<260),b(0<b<260),其中 a , b a,b a,b 分别表示分数的分子和分母,中间用空格隔开
输出格式
输出仅一个数,即最少使用的 1 1 1阻值的电阻个数
样例输入
26 11
样例输出
8
数据范围和提示
样例解释: 8 8 8个电阻的方案如右: 3 3 3 个电阻并联,然后和 1 1 1个电阻串联,再依次和 2 2 2个电阻并联,最后和 2 2 2个电阻串联,经计算阻值为 26 11 \frac{26}{11} 1126,且 7 7 7个电阻不能达到此目的
数据范围:
对于 5 % 5\% 5% 的数据满足 a = 0 a=0 a=0
另有 5 % 5\% 5% 的数据满足 a = 1 , a ≠ b a=1,a≠b a=1,a̸=b
另有 5 % 5\% 5% 的数据满足 b = 1 , a ≠ b b=1,a≠b b=1,a̸=b
另有 5 % 5\% 5% 的数据满足 a = b a=b a=b
另有 30 % 30\% 30% 的数据满足 a ≤ 10 , b ≤ 10 a≤10,b≤10 a10,b10
对于 100 % 100\% 100% 的数据满足 0 ≤ a &lt; 2 60 , 0 &lt; b &lt; 2 60 0≤a&lt;2^{60},0&lt;b&lt;2^{60} 0a<260,0<b<260
温馨提示:此题可能用到 long long 类型,请使用 %lld 占位符输入输出或使用 cin,cout

看上去是一道物理题,然而是一道规律/脑洞题。
考场上并没有想出来什么好的做法,于是写了个50分的暴力递推。递推的方式是每次加一个电阻,枚举并联和串联两种方式。
正解如下。
考虑一个连分数

[ a 0 ; a 1 , a 2 , a 3 , … , a n ] = a 0 + 1 a 1 + 1 a 2 + 1 a 3 … = a b [a_0; a_1,a_2,a_3,\ldots ,a_n] = a_0 + \frac{1}{a1+\frac{1}{a2+\frac{1}{a_3\ldots}}} = \frac{a}{b} [a0;a1,a2,a3,,an]=a0+a1+a2+a3111=ba

可以表示** a n a_n an个电阻串联后与 a n − 1 a_{n-1} an1个电阻串联的电路并联,然后与 a n − 2 a_{n-2} an2个电阻串联的电路并联,……然后与 a 1 a_1 a1个电阻串联的电路并联,最后与 a 0 a_0 a0个电阻串联**,其最终所得的电阻。
可以证明,这种构造方式一定是符合要求的方案中所用电阻最少的。
我们记获取阻值为 a b \frac{a}{b} ba的电阻所需最少电阻数为 f ( a , b ) f(a,b) f(a,b)
容易得到如下递归式:

f ( a , b ) { a b ( b ∣ a ) f ( b , a &VeryThinSpace; m o d &VeryThinSpace; b ) + ⌊ a b ⌋ ( b ∤ a ) f(a,b)\left\{ \begin{aligned} \frac ab &amp; &amp; (b\mid a) \\ f(b, a\bmod b) + \lfloor \frac ab \rfloor &amp; &amp; (b\nmid a) \end{aligned} \right. f(a,b)baf(b,amodb)+ba(ba)(ba)

显然该算法与欧几里得算法的渐进复杂度相同,为 O ( l o g N ) O(logN) O(logN)

#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 n,牛顿的树与他现在的智商成正比,所以 n ≤ 300000 n≤300000 n300000
接下来 n − 1 n−1 n1 行,每行两个整数 u , v u,v u,v,表示树上的一条边
由于你不可能既知道牛顿的智商,又通晓他的心思(牛顿不确定性原理),所以这是一棵无根树(废话)。
于是有一个整数 m m m,表示牛顿的思考次数,他最多可以在 1 1 1 秒钟内思考 300000 300000 300000
以下 m m m 行数据格式如下
首先由一个数 k k k 表示类型
k = 1 k=1 k=1 时,有两个整数 u , v u,v u,v,牛顿通过架设一个三棱镜遮住树边 u , v u,v u,v,于是 u , v u,v u,v 这条边从树上消失(保证这条边之前存在)。
k = 2 k=2 k=2 时,撤销上一次放置的三棱镜。
k = 3 k=3 k=3 时,牛顿想知道树上的两个节点 u , v u,v u,v 是否连通。
而你,是R国的某只程序员,你国的尤里X已经成功地读取到了牛顿的思考过程,你需要复现 k = 3 k=3 k=3 的结果,以供你国进行分析
输入格式
输入文件的第一行为一个整数 n n n ,表示树的节点数
接下来的 n − 1 n−1 n1 行,每行整数 u , v u,v u,v ,表示 u , v u,v u,v 有一条连边
接下来一行有一个整数 m m m ,表示操作与询问数
接下来的 m m m 行,每行首先给出一个整数 k k k
对于 k = 1 k=1 k=1,给出两个整数 u , v u,v u,v ,表示架设三棱镜摧毁 u , v u,v u,v 这条边,保证这条边之前存在
对于 k = 2 k=2 k=2,表示撤销上一次的三棱镜,并保证上一次的三棱镜存在
对于 k = 3 k=3 k=3,给出两个整数 u , v u,v u,v,询问 u , v u,v u,v 两点是否连通
输出格式
对于每个 k = 3 k=3 k=3 的操作,若 u , v u,v 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 % 20\% 20% 的数据, n ≤ 1000 , m ≤ 1000 n≤1000,m≤1000 n1000,m1000
对于另外 50 % 50\% 50% 的数据, k ≠ 2 k≠2 k̸=2
对于 100 % 100\% 100% 的数据, n ≤ 300000 , m ≤ 300000 , 1 ≤ u , v ≤ n n≤300000,m≤300000,1≤u,v≤n n300000,m300000,1u,vn

裸数据结构题,大意是维护一个森林 / 一些不相交集集合 的 连通性 / 从属关系。
对于一半的数据,只有拆分操作,没有联通操作。我们可以将操作倒序,于是就变成了一道轻松愉快的并查集水题。
对于全部数据,如果看作森林处理,可以写LCT或者树链剖分。然而,这道题具有一定的特殊性,即联通操作的对象一定是最近一次拆分操作的对象,针对这个性质,我们也可以写可持久化并查集。
这里上LCT代码。复杂度均摊 O ( N l o g N ) O(NlogN) O(NlogN)

#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;
}

最短的子区间(学长的题)

问题描述
(不要问我“最短”是谁
给你一个关键字集合
( k e y 1 , k e y 2 , . . . , k e y m ) (key1,key2,...,keym) (key1,key2,...,keym)
和一个单词序列
( w o r d 1 , w o r d 2 , . . . , w o r d n ) (word1,word2,...,wordn) (word1,word2,...,wordn)
请在这个单词序列中找到最短的子序列能够包含所有的关键字 (可以忽略关键字顺序)
序列最短即这个序列中包含的单词数最少
输入格式
有多组数据,每组数据占三行
对于每组数据:
第一行包含两个整数 n , m n,m n,m, 分别代表关键字集合的大小和单词序列的长度
第二行包含 m m m 个用空格隔开的关键字
第三行包含 n n n 个用空格隔开的单词
输入数据以 E O F EOF EOF 作为结束标志
注意: 每个关键字和单词的长度都小于 100 100 100, 关键字保证互不相同
输出格式
对于第 X 组测试数据:
如果存在这样的最短子序列, 则输出类似于 Case X: XX
如果不存在则只需要输出 Case X: -1
X 是从 1 1 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 ≤ 1 0 5 , 1 ≤ m ≤ 100 1≤n≤10^5,1≤m≤100 1n105,1m100

首先,字符串匹配是必不可少的,对于该题这种多模板字符串匹配,我们可以考虑AC自动机(其实可以当作一般的Trie),当然也可以用hash。
匹配结束后,我么可以得到单词序列中每个单词所匹配的关键字序号。然后使用尺取法,维护一个始终向右移动的区间,不断更新包含所有关键字的子序列的最短长度。
总复杂度平均情况下 O ( l n ) O(ln) O(ln)( l l l即单词长度)
(起初我在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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用Crypto++库中的RSA类来导入RSA公钥/私钥的字符串表示。下面是一个简单的示例代码: ```c++ #include <iostream> #include <string> #include <cryptopp/rsa.h> #include <cryptopp/base64.h> using namespace CryptoPP; int main() { // RSA keys in PEM format std::string publicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvRJ9YmJ9A0seV0bSf+I6\n" "n5i6dF6qXl5Hf8b0KJvJ7eN0yTnE4t5rKQYdM+DLg1yXz+4kWv8xgJxJW9Q2gY7L\n" "wNqROnFJZrOq6zZVrWXfR4gWVv8grl2PZl6vQfGbdzPd2BhLW1QOyPZfGy1CCoMg\n" "FQd6ZqBx1UJHsUSx/PnV0i35S8zO7Zq9UxOYbRr9SiCsTJ0FNLwPz7CkC5u3VJ5D\n" "z5z2K7U0fGkO+8fOc5Qv2JUqKd6sN2oT+18+eVf/lvtlQ4U3aXyKZB7u0k8T3xKx\n" "3J2r3x4y+o9B3fz1aV0R/5N5Iz2Kt6gJyJmJyNfh0T+D5JLZL8o+Oq8rD8/8mLgV\n" "XQIDAQAB\n" "-----END PUBLIC KEY-----\n"; std::string privateKeyPEM = "-----BEGIN PRIVATE KEY-----\n" "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCuN1Qa1iRQ9ZvZ\n" "iFfZjzKfKXk9X5Rq8Dp4CCVt1LJbXG8oU1Kd7UeJb+JyQrK+D1hViDUKvC4pDvZL\n" "7Wv8wzZ1mJN+3M+Vc7dqSaTr0z+U4d3ZKQKj1UjF9/3Ud/HXw6Ez84Zx5fX1U7P\n" "3u9Qo+6g0cva0tYcZ8E4m8osjzOZn3n5Z+qoDZ2IuoktDjQOdT7vZJc7JmLQ+6U\n" "Gyf8B4j0c6V7iJ0oqOeq27m2xY6xu9gKwDvZl9zJrFv6f9JQy6yY5ZjXVItK8o+\n" "x3DfX+eD8XO7Z0f1wO3J5lXgBuJop4B3PzZPmZw3jwN3LZwU7hAqjWQvLj5r+DqR\n" "KwvS6TJnAgMBAAECggEBAJv9zZ1N9sN7E+gTt9y/7xFG2PjgB9VjYWg6bd4QKpRq\n" "zGJNj2n8q6k8l7k2uT6sRcO5RhS8bQpO4iLl3GJXZr5kjC0Z/1kE1p4H8Y9e5bDh\n" "gNl3ZnK3Wpli8q7KqUjOY8j6Ktj7+vZnT3sW6v/qpIbQgBn7d3q0qsqzjTm8c5f\n" "V3JzUJqkVJqX1mJmJd6tZslpO8mJyZL/vkC4KszkQeE8xKu6sIiA4r6l5e0+OJ8M\n" "8XmXhV8D8ZcHgKvq7qrBvFq5Xv2mHsLdQO+J7uNLC3iQzY3u4jB6Gk8fN7N3e3jv\n" "dDmf8P0l7dOj6q5sZtqoK6fJjW+PcVnC8KzXUy4YrtkCgYEA3g9W5c7J8KmX3xSU\n" "2p/7VfGzBYC7J1Ll1fz4qWf3XZ2VQF7q6yRrA5X+H2GkZiB3vL8/3gOeO3WuGnI0\n" "bJr1lOY9xMvQj7a6KdC5h4Wvmt3qqh6VdQw7dFx8fJ8x5RQwReOuyZ9nEE5Vf+en\n" "cRg9Uk9LQl3rZ4cazH7c+Lkrj2ECgYEAzL4u8eJ5gCpUvJZJyCmlrJX0GryL1wzE\n" "5zO8m1tjwg1YONyJvR4g4bd7p+56q6v3wTzoumH6x3AWDZC8Z6Jr6i4a8+cnl+9M\n" "P+Jr5Lr2fN2Y7w3dFzHhY5NIR0ZxTQJdC5Jp+el1+Kg2y4eFJZpQz0ueH2Jy6wuz\n" "zqJUQY2RmXUCgYEAwYjQg1FnKPGvJlRrXwL9cO4I3W0gx/kD+LpT8aEguJlRf4uS\n" "ZyN8a+xLZbGzWc5wKT9z3whYf4fXf4aFh2U+ZQzXG7yUa5KdNf7xvP8QXa6omQY0\n" "sQO2e1+Vc1r7h2e0HNY0U6E0g+7l7MBZv5lZP6XG7HdY5k8n1UdbvR9uGkECgYEA\n" "xAIlWc+0qLQUjY7t2sqHjzJ3nV2GxMvW0G9fZgD6loQWtKX2Y7r/x6WLZ9Smc5ZQ\n" "4QJ1l0sT1q2YKqCwR1u+2J9lX8Pp17FZK8Xvq8kPT9lQK9o3+JvUqPvX6xrmJ2mK\n" "R7uxvYv+ZzL2R9gkt/6g1QH0aRZzZj0+6+J5PfWen9sCgYEA7fQxJYJdQdUgJvZL\n" "gH2Y1o5LJ5W1N3v9zQJLJGJ1Tt7TfB3gXzOzL/hsW68Uzr+1JqP6e9LTk5OuWVQG\n" "yR7zFv3p6K5tqT2Z0OJdS4xVw/2zB5YzJiK6I6b5aMZr9o2ZtW/0A1m5RcU6bJSo\n" "Vz5Q5O1ZoD7JdS3d+6iNq+f8dQ==\n" "-----END PRIVATE KEY-----\n"; // Base64 decode the PEM strings std::string publicKeyStr, privateKeyStr; StringSource(publicKeyPEM, true, new Base64Decoder(new StringSink(publicKeyStr))); StringSource(privateKeyPEM, true, new Base64Decoder(new StringSink(privateKeyStr))); // Load the keys from the decoded strings RSA::PublicKey publicKey; RSA::PrivateKey privateKey; StringSource(publicKeyStr, true, new PEMDecoder(new StringSink(publicKey))); StringSource(privateKeyStr, true, new PEMDecoder(new StringSink(privateKey))); return 0; } ``` 在这个示例中,我们将PEM格式的RSA公钥/私钥字符串进行Base64解码,然后使用Crypto++库中的PEMDecoder类将它们加载到RSA::PublicKey和RSA::PrivateKey对象中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值