[UOJ#12]【UER #1】猜数
试题描述
这一天,小Y、小D、小C正在愉快地玩耍。
小Y是个数学家,他一拍脑袋冒出了一个神奇的完全平方数 n。
小D是个机灵鬼,很快从小Y嘴里套出了 n的值。然后在脑内把 n写成了 a×b的形式。其中 a,b 都是正整数。
小C是个八卦狂,他发现小D从小Y那里获知了神奇的东西,于是死缠烂打追问小D。最后小D说道:“我可以告诉你正整数 g和 l的值,我保证 ab=gl=n且 a,b都是 g 的倍数。但是 a,b 我可不能告诉你。”
这可急坏了小C。他决定退而求其次,找出 a+b 的最小值和最大值。请你帮帮他吧!
输入
第一行一个正整数 T,表示有 T组询问。
接下来 T行每行两个正整数 g,l 表示一组询问。
输出
对于每个询问输出一行两个正整数,分别表示 a+b的最小值与最大值。保证问题有解。
C/C++ 输入输出 long long 时请用 %lld
。C++ 可以直接使用 cin/cout 输入输出。
输入示例1
1 1 4
输出示例1
4 5
输入示例2
1 2 8
输出示例2
8 10
数据规模及约定
g, l ≤ 1018
题解
注意 n 是完全平方数。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; #define LL long long const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } LL read() { LL x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 70 LL prime[maxn]; int cnt, c[maxn]; int main() { int T = read(); while(T--) { LL g = read(), l = read() / g; printf("%lld %lld ", g * 2 * (LL)sqrt(l), g * (l + 1)); } return 0; }
[UOJ#13]【UER #1】跳蚤OS
试题描述
跳蚤OS 是跳蚤国自主研发的功能强大的操作系统。
跳蚤OS的文件系统与普通的文件系统类似,是个文件夹套文件夹的结构。文件系统根目录称为“/”。我们可以用文件路径来表明文件所在的位置,比如“/flea/uoj”表示根目录下的flea文件夹下的uoj文件。
跳蚤OS的文件系统中。快捷方式是一种特殊的文件夹,点开该快捷方式相当于打开该快捷方式指向的文件夹。
比如,如果有一个快捷方式 “/etc/abc”,该快捷方式指向 “/flea/def”这个文件夹,那么一旦访问“/etc/abc”就相当于访问“/flea/def”。
这一天,跳蚤国王正在使用跳蚤OS。初始时文件系统为空,只有根目录。他每次会进行如下操作:
- 首先,随便写出两个文件路径 s和 t。
- 接着,如果位置 t处不存在文件,则在该处创建一个空文件夹。
- 最后,跳蚤国王保证 s这个位置没有文件,于是在 s 处创建一个快捷方式指向 t 。如果 t 是个快捷方式,那么 s 将指向 t 所指向的文件夹。
上文所说的“创建”在父级目录不存在的时候要一并创建其父级目录。比如,假设文件系统里只有 “/v ” 这个文件夹,那么现在我创建 “/v/flea/king/qaq” 就会在文件系统中新增三个文件夹:“/v/flea”, “/v/flea/king”, “/v/flea/king/qaq”。
跳蚤国王进行了 n 次这样的操作后,开始不断问他的助手伏特:现在我如果在 p 这个路径处创建一个文件夹(如果已存在则不创建),那么这个文件夹的真实路径是什么?
于是伏特只好向你求助了,请你帮一帮他吧!请参照样例来更清晰地理解题意。
输入
第一行两个正整数n,m,表示跳蚤国王进行了n个操作,提了m个问题。
接下来n行每行两个用空格隔开的字符串s,t,表示跳蚤国王的一次操作。
接下来 m 行每行一个字符串 p 表示跳蚤国王的一个询问。
保证所有的 s,t,p 都是合法的文件路径。即,文件夹名一定是由小写英文字母组成的非空字符串,路径名一定形如“/xxx/xxx/xxx/.../xxx”这样子。保证当路径不为根目录“/”时,路径不以“/”结尾。
输出
对于跳蚤国王的每个询问输出真实路径。
输入示例1
6 5
/root /
/duliu /picks
/vfk /vfleaking
/orz/orz/orz /duliu
/otl /duliu/duliu
/vfk/sb /vfleaking
/vfk/sb/nothing/nothing
/orz
/orz/orz/orz
/qaq
/otl
输出示例1
/vfleaking/nothing/nothing
/orz
/picks
/qaq
/picks/duliu
输入示例2
2 3 /ba/la / /w/o/w /w /ba/la/ba/la/ba/la/ba/la/ba/la/ba/la/ba/la /ba/la/ba/la/ba/la/ba/la/ba/la/ba/la/ba/la/ba /w/o/w/o/w/o/w/o
输出示例2
/
/ba
/w/o
输入示例3
输出示例3
数据规模及约定
n ≤ 20000, m ≤ 10
题解
建立一棵 Trie 树,用类似AC自动机的思想,但是把 Fail 指针指向快捷方式所指的源文件。
注意这里的 Fail 指针仅在下一个字符为“/”生效,还有一些细节需要注意,这里不赘述了。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 500010 #define maxs 27 int n, q; char S[maxn], A[maxn], B[maxn]; int ToT = 1, f[maxn], fa[maxn], ch[maxn][maxs], Q[maxn], hd, tl; int idx(char t) { return t == '/' ? 26 : t - 'a'; } int main() { // freopen("ex_os3.in", "r", stdin); // freopen("data.out", "w", stdout); n = read(); q = read(); for(int i = 1; i <= n; i++) { scanf("%s", S + 1); int len = strlen(S + 1); int u = 1; for(int j = 1; j <= len; j++) { int d = idx(S[j]); if(isalpha(S[j])) { if(!ch[u][d]) ch[u][d] = ++ToT, fa[ToT] = u; u = ch[u][d]; } else { while(f[u]) u = f[u]; if(!ch[u][d]) ch[u][d] = ++ToT, fa[ToT] = u; u = ch[u][d]; } } while(f[u]) u = f[u]; scanf("%s", S + 1); len = strlen(S + 1); int u2 = u; u = 1; for(int j = 1; j <= len; j++) { int d = idx(S[j]); if(isalpha(S[j])) { if(!ch[u][d]) ch[u][d] = ++ToT, fa[ToT] = u; u = ch[u][d]; } else { while(f[u]) u = f[u]; if(!ch[u][d]) ch[u][d] = ++ToT, fa[ToT] = u; u = ch[u][d]; } } while(f[u]) u = f[u]; if(len == 1) u = 1; f[u2] = u; // printf("%d %d ", u2, u); } while(q--) { scanf("%s", S + 1); int len = strlen(S + 1); int u = 1, ca = 0, cb = 0; bool flag = 1; for(int i = 1; i <= len; i++) { int d = idx(S[i]); if(isalpha(S[i])) { if(!ch[u][d]) { flag = 0; for(; i <= len; i++) B[++cb] = S[i]; break; } u = ch[u][d]; } else { while(f[u]) u = f[u]; if(!ch[u][d]) { for(; i <= len; i++) B[++cb] = S[i]; break; } u = ch[u][d]; } } if(flag) while(f[u]) u = f[u]; while(fa[u]) { for(int i = 0; i < maxs; i++) if(ch[fa[u]][i] == u) { A[++ca] = i == 26 ? '/' : i + 'a'; break; } u = fa[u]; } // printf("ans: "); for(int i = ca; i; i--) putchar(A[i]); for(int i = 1; i <= cb; i++) putchar(B[i]); if(ca + cb == 0) putchar('/'); putchar(' '); } return 0; }
[UOJ#14]【UER #1】DZY Loves Graph
试题描述
DZY开始有 n 个点,现在他对这 n 个点进行了 m 次操作,对于第 i 个操作(从 1 开始编号)有可能的三种情况:
- Add a b: 表示在 a 与 b 之间连了一条长度为 i 的边(注意,i 是操作编号)。保证 1≤a,b≤n。
- Delete k: 表示删除了当前图中边权最大的k条边。保证 k 一定不会比当前图中边的条数多。
- Return: 表示撤销第 i−1 次操作。保证第 1 次操作不是 Return 且第 i−1 次不是 Return 操作。
请你在每次操作后告诉DZY当前图的最小生成树边权和。如果最小生成树不存在则输出 0 。
输入
第一行两个正整数 n,m 。表示有 n 个点 m 个操作。 接下来 m 行每行描述一个操作。
输出
对于每一个操作输出一行一个整数表示当前最小生成树边权和。
输入示例1
2 2 Add 1 2 Return
输出示例1
1 0
输入示例2
5 10 Add 2 1 Add 3 2 Add 4 2 Add 5 2 Add 2 3 Return Delete 1 Add 2 3 Add 5 2 Return
输出示例2
0 0 0 10 10 10 0 0 15 0
输入示例3
输出示例3
数据规模及约定
n≤3×105, m≤5×105
题解
仔细读题,发现可以做到每条边最多加进来一次,删除一次。(想一想,为什么)
有删除操作了,不能用路径压缩版并查集了,那么考虑用按秩合并的并查集做。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *tail; inline char Getchar() { if(Head == tail) { int l = fread(buffer, 1, BufferSize, stdin); tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 300010 #define maxm 500010 #define LL long long int n, m, cnt; LL ans[maxm], sum[maxm]; bool yes[maxm]; struct Edge { int u, v; } es[maxm]; struct Que { int tp, a, b; } qs[maxm]; int pa[maxn], siz[maxn]; int findset(int x) { return x == pa[x] ? x : findset(pa[x]); } void Union(int a, int b, int i) { a = findset(a); b = findset(b); es[++cnt] = (Edge){ a, b }; sum[cnt] = sum[cnt-1]; yes[cnt] = yes[cnt-1]; if(a == b) { ans[i] = ans[i-1]; return ; } if(siz[a] > siz[b]) swap(a, b); siz[b] += siz[a]; pa[a] = b; sum[cnt] += (LL)i; yes[cnt] = (siz[b] == n); ans[i] = (siz[b] == n) ? sum[cnt] : 0; return ; } void Del(int x) { int a = es[x].u, b = es[x].v; if(a == b) return ; if(siz[a] > siz[b]) swap(a, b); siz[b] -= siz[a]; pa[a] = a; pa[b] = b; return ; } int main() { n = read(); m = read(); for(int i = 1; i <= m; i++) { char tc = Getchar(); while(!isalpha(tc)) tc = Getchar(); if(tc == 'A') qs[i].tp = 1, qs[i].a = read(), qs[i].b = read(); if(tc == 'D') qs[i].tp = 2, qs[i].a = read(), qs[i].b = -1; if(tc == 'R') { qs[i].tp = 3; while(isalpha(tc)) tc = Getchar(); } } for(int i = 1; i <= n; i++) pa[i] = i, siz[i] = 1; for(int i = 1; i <= m; i++) { if(qs[i].tp == 3) continue; if(qs[i].tp == 1) { Union(qs[i].a, qs[i].b, i); if(i < m && qs[i+1].tp == 3) Del(cnt--), ans[i+1] = yes[cnt] ? sum[cnt] : 0; } if(qs[i].tp == 2) { int k = qs[i].a; if(i < m && qs[i+1].tp == 3) { ans[i+1] = ans[i-1]; ans[i] = yes[cnt-k] ? sum[cnt-k] : 0; } else { for(int j = cnt; j >= cnt - k + 1; j--) Del(j); cnt -= k; ans[i] = yes[cnt] ? sum[cnt] : 0; } } } for(int i = 1; i <= m; i++) printf("%lld ", ans[i]); return 0; }