【UER #1】[UOJ#12]猜数 [UOJ#13]跳蚤OS [UOJ#14]DZY Loves Graph

时间:2021-11-10 12:40:26

[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

 

输入示例2

 

输出示例2

 

数据规模及约定

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\n", 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。初始时文件系统为空,只有根目录。他每次会进行如下操作:

  1. 首先,随便写出两个文件路径 s和 t。
  2. 接着,如果位置 t处不存在文件,则在该处创建一个空文件夹。
  3. 最后,跳蚤国王保证 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

/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\n", 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('\n');
} return 0;
}

[UOJ#14]【UER #1】DZY Loves Graph

试题描述

DZY开始有 n 个点,现在他对这 n 个点进行了 m 次操作,对于第 i 个操作(从 1 开始编号)有可能的三种情况:

  1. Add a b: 表示在 a 与 b 之间连了一条长度为 i 的边(注意,i 是操作编号)。保证 1≤a,b≤n。
  2. Delete k: 表示删除了当前图中边权最大的k条边。保证 k 一定不会比当前图中边的条数多。
  3. Return: 表示撤销第 i−1 次操作。保证第 1 次操作不是 Return 且第 i−1 次不是 Return 操作。

请你在每次操作后告诉DZY当前图的最小生成树边权和。如果最小生成树不存在则输出 0 。

输入

第一行两个正整数 n,m 。表示有 n 个点 m 个操作。 接下来 m 行每行描述一个操作。

输出

对于每一个操作输出一行一个整数表示当前最小生成树边权和。

输入示例1

Add
Return

输出示例1


输入示例2

Add
Add
Add
Add
Add
Return
Delete
Add
Add
Return

输出示例2


输入示例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\n", ans[i]); return 0;
}