Problem 1 东风谷早苗(robot.cpp/c/pas)
题目描述 在幻想乡,东风谷早苗是以高达控闻名的高中生宅巫女。某一天,早苗终于入手了最新款的钢达姆模型。作为最新的钢达姆,当然有了与以往不同的功能了,那就是它能够自动行走,厉害吧(好吧,我自重)。早苗的新模型可以按照输入的命令进行移动,命令包含’E’、’S’、’W’、’N’四种,分别对应四个不同的方向,依次为东、南、西、北。执行某个命令时,它会向着对应方向移动一个单位。作为新型机器人,自然不会只单单执行一个命令,它可以执行命令串。对于输入的命令串,每一秒它会按照命令行动一次。而执行完命令串最后一个命令后,会自动从头开始循环。在0时刻时早苗将钢达姆放置在了(0,0)的位置,并且输入了命令串。她想要知道T秒后钢达姆所在的位置坐标。
输入格式 第1行:一个字符串,表示早苗输入的命令串,保证至少有1个命令
第2行:一个正整数T
输出格式 第1行:两个整数,表示T秒时,钢达姆的坐标
输入样例 NSWWNSNEEWN
12
输出样例 -1 3
数据范围 对于60%的数据:T <= 500,000且命令串长度 <= 5,000
对于100%的数据:T <= 2,000,000,000且命令串长度<= 5,000
注意 向东移动,坐标改变改变为(X+1,Y);
向南移动,坐标改变改变为(X,Y-1);
向西移动,坐标改变改变为(X-1,Y);
向北移动,坐标改变改变为(X,Y+1);
Robot 东风谷早苗
题目大意 读入一串长度为N的命令字符’N’、’E’、’W’、’S’。每一秒钟按照命令移动一次,不断重复,求问T秒之后,所在位置的坐标。起始位置为(0,0),地图无穷大。
考察算法 字符串处理 数学题
算法一 从第0秒开始模拟每一秒的位置。
时间复杂度:O(T) 期望得分:60
算法二 由于命令串是重复执行的,所以我们可以先计算出一个周期后机器人的移动量。比如在第一个周期后,机器人从(0,0)移动到(△X,△Y),那么每个周期之后机器人坐标的改变为(X+△X,Y+△Y)。设T/N = A…B,那么机器人一共会运行的周期数为A,所以在A个周期后,机器人的坐标为(A*△X,A*△Y)。之后机器人还会移动B步,此时就可以直接在(A*△X,A*△Y)的基础上枚举这B步的移动。
时间复杂度:O(N) 期望得分:100
#includeusing namespace std; #define MAXL 5009 const int dr[4][2] = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}}; char command[ MAXL ]; int TimeLimit; int main() { scanf("%s", command); scanf("%d", &TimeLimit); int cycle, rest, times, order, delta[2], last[2]; cycle = strlen( command ); rest = TimeLimit % cycle; times = TimeLimit / cycle; delta[0] = delta[1] = 0; last[0] = last[1]= 0; for (int i = 0; i < cycle; i++) { switch ( command[i] ) { case 'E' : order = 0; break; case 'S' : order = 1; break; case 'W' : order = 2; break; case 'N' : order = 3; break; default : break; } for (int j = 0; j <= 1; j++) { delta[j] += dr[ order ][j]; if (i < rest) last[j] += dr[ order ][j]; } } for (int j = 0; j <= 1; j++) last[j] += times * delta[j]; printf("%d %d\n", last[0], last[1]); return 0; }
Problem 2 西行寺幽幽子(spring.cpp/c/pas)
题目描述 在幻想乡,西行寺幽幽子是以贪吃闻名的亡灵。不过幽幽子可不是只会吃,至少她还管理着亡灵界。话说在幽幽子居住的白玉楼有一颗常年不开花的樱树——西行妖。幽幽子决定去收集人间的春度,聚集起来让西行妖开花。很快,作为幽幽子家园艺师的魂魄妖梦收集到了M个单位的春度。并且在这段时间里,幽幽子计算出要让西行妖开出一朵花需要N个单位的春度。现在幽幽子想要知道,使用所有的春度,能够让西行妖开出多少朵花。
输入格式 第1行:一个正整数M
第2行:一个正整数N
N,M的位数不超过L,L的范围在题目后面给出
输出格式 第1行:一个整数ans,表示能开出花的朵数
输入样例 73861758
12471
输出样例 5922
数据范围 对于60%的数据:L <= 2,000且ans <= 2,000
对于100%的数据:L <= 20,000且ans <= 2,000,000,000
Spring 西行寺幽幽子
题目大意 高精度数N除以高精度数M,求商
考察算法 高精度数 二分答案/二进制拆分
算法一 从0开始枚举累加,直到当前值超过了N。此算法只需要使用加法和比较。
对于这种算法是否使用压位高精度给分相同。
时间复杂度:O(AnsL) 期望得分:60
算法二 在枚举商的时候采用二分答案的方法。如果M和Mid的乘积小于或等于N,Check的值为True;否则为False。二分的伪代码如下:
While (Left + 1 < Right)
Begin
Mid <- (Left + Right) / 2;
if Check(Mid) Then Left <- Mid;
Else Right <- Mid;
End
时间复杂度:O(LlogAns) 期望得分:100
算法三 因为Ans可以转化成二进制的形式,建立高精度数组t[32],t[i] = 2^i * M。从高位向低位进行枚举,如果对于当前位k,N >=t[k],那么Ans加上2^k,N减去t[k]。其本质相当于Ans = 2^p[1 +2^p[2]+ … +2^p[j],而N = 2^p[1] *M+2^p[2]*M+…+2^p[j]*M+rest(rest < M),p数组是将Ans表示为二进制后该位为1的位置。比如5(10) = 101(2),那么5所对应的p数组为{1,3}。
由于我们需要求的是Ans的值,所以必须从高位向低位枚举,否则会出现错误。如果有疑问,读者可以尝试从低位向高位枚举,看看输出的结果。
时间复杂度:O(LlogAns) 期望得分:100
#includeusing namespace std; #define MAXL 20009 typedef int hp[ MAXL ]; char str[ MAXL ]; hp first, second, now, t[ 32 ]; inline int max(int x, int y) { return x > y ? x : y; } struct HP { hp box; void Str_to_hp(char *str, hp &a) { Set(a); a[0] = strlen( str ); for (int i = 1; i <= a[0]; i++) a[i] = str[ a[0] - i ] - '0'; return ; } void Set(hp &a) { memset(a, 0, sizeof(a)); a[0] = 1; return ; } int Compare(hp a, hp b) { if (a[0] > b[0]) return 1; if (a[0] < b[0]) return -1; for (int i = a[0]; i >= 1; i--) { if (a[i] > b[i]) return 1; if (a[i] < b[i]) return -1; } return 0; } void Add(hp a, hp b, hp &c) { memset(box, 0, sizeof(box)); box[0] = max(a[0], b[0]); for (int i = 1; i <= box[0]; i++) box[i] = a[i] + b[i]; box[0]++; for (int i = 1; i <= box[0]; i++) if (box[i] >= 10) box[i + 1]++, box[i] %= 10; while (!box[ box[0] ] && box[0] > 1) box[0]--; memcpy(c, box, sizeof(c)); return ; } void Sub(hp a, hp b, hp &c) { memset(box, 0, sizeof(box)); box[0] = a[0]; for (int i = 1; i <= box[0]; i++) box[i] = a[i] - b[i]; for (int i = 1; i <= box[0]; i++) if (box[i] < 0) box[i + 1]--, box[i] += 10; while (!box[ box[0] ] && box[0] > 1) box[0]--; memcpy(c, box, sizeof(box)); return ; } } Op; int main() { int ans = 0; scanf("%s", str); Op.Str_to_hp(str, first); scanf("%s", str); Op.Str_to_hp(str, second); memcpy(t[1], second, sizeof(t[1])); for (int i = 2; i <= 31; i++) Op.Add(t[i - 1], t[i - 1], t[i]); for (int i = 31; i >= 1; i--) if (Op.Compare(first, t[i]) == 1) { ans |= 1 << (i - 1); Op.Sub(first, t[i], first); } printf("%d\n", ans); return 0; }
Problem 3 琪露诺(iceroad.cpp/c/pas)
题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只会移动到i+L到i+R中的一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。开始时,琪露诺在编号0的格子上,只要她下一步的位置编号大于N就算到达对岸。
输入格式 第1行:3个正整数N, L, R
第2行:N+1个整数,第i个数表示编号为i-1的格子的冰冻指数A[i-1]
输出格式 第1行:一个整数,表示最大冰冻指数。保证不超过2^31-1
第2行:空格分开的若干个整数,表示琪露诺前进的路线,最后输出-1表示到达对岸
输入样例 5 2 3
0 12 3 11 7 -2
输出样例 11
0 3 -1
数据范围 对于60%的数据:N <= 10,000
对于100%的数据:N <= 200,000
对于所有数据 -1,000 <= A[i] <= 1,000且1 <= L <= R <= N
注意 此题采用Special Judge
Iceroad 琪露诺
题目大意 存在一行格子,有各自的分值。从当前一格可以移动到前面L到R范围内的格子,并取得当前分值。求从左到右的最大得分即方案。
考察算法 Dp 优先队列/单调队列
算法一 从1到N行进行枚举,状态转移方程为:
f[i] = Max{f[j]} + A[i]; (i – R <= j <= i - L)
最后取从f[N – R + 1]到f[N]中的最大值。
方案的输出可采用数组prev[]记录,prev[i]记录计算f[i]时所用的j的值。
时间复杂度:O(N^2) 期望得分:60
算法二 在算法一的基础上使用优先队列或线段树取出[i – R, i – L]中的最大值,具体实现这里就不说明了。在标程中,优先队列使用的是大根堆。
时间复杂度:O(NlogN) 期望得分:100
算法三
维护一个在[i-R,i-L]内f[i]单调递减的决策队列:Q[i]记录决策位置,当队首元素位置小于i-R时队首出队,当f[i-l+1]优于队尾决策时队尾出队,然后将新的f[i-l+1]加入队尾。易知此时f[Q[head]]即为Max{f[j]|i-R <= j <= i-L},f[i]可直接通过队首位置转移求得,转移代价为O(1)。
(此解法由Wagner提供,具体实现pas)
时间复杂度:O(N) 期望得分:100
#includeusing namespace std; #define MAXN 200009 #define Inf 999999999 bool step[ MAXN ]; int ice[ MAXN ], prev[ MAXN ], f[ MAXN ]; int N, L, R; struct HEAP { struct DATA { int from, key; } heap[ MAXN ]; int pos[ MAXN ]; int size; void Swap(int x, int y) { DATA box; box = heap[x]; heap[x] = heap[y]; heap[y] = box; pos[ heap[x].from ] = x; pos[ heap[y].from ] = y; return ; } void HeapUp(int x) { int tp; while (x != 1) { tp = x >> 1; if (heap[x].key < heap[tp].key) break; Swap(tp, x); x = tp; } return ; } void HeapDown(int x) { int tp; while (x * 2 <= size) { tp = x << 1; if (tp < size) tp += (heap[tp].key < heap[tp + 1].key); if (heap[x].key > heap[tp].key) break; Swap(tp, x); x = tp; } return ; } void Insert(int ID) { ++size; heap[ size ].from = ID; heap[ size ].key = f[ID]; pos[ ID ] = size; HeapUp( size ); return ; } void Delete(int ID) { int tp = pos[ID]; if (tp == size) { size--; return ; } heap[tp] = heap[ size ]; pos[ heap[tp].from ] = tp; size--; HeapUp(tp); HeapDown(tp); return ; } int Get() { return heap[1].from; } } Heap; int main() { scanf("%d %d %d", &N, &L, &R); for (int i = 0; i <= N; i++) scanf("%d", &ice[i]); for (int i = 1; i < L; i++) f[i] = -Inf; for (int i = L; i <= N; i++) { if (i - L >= 0) Heap.Insert(i - L); if (i - R - 1 >= 0) Heap.Delete(i - R - 1); prev[i] = Heap.Get(); if (f[ prev[i] ] != -Inf) f[i] = f[ prev[i] ] + ice[i]; else f[i] = -Inf; } int best = -1; for (int i = N - R + 1; i <= N; i++) if (best == -1 || f[i] > f[ best ]) best = i; printf("%d\n", f[ best ]); do { step[ best ] = true; best = prev[ best ]; } while (best); step[0] = true; for (int i = 0; i <= N; i++) if (step[i]) printf("%d ", i); printf("-1\n"); return 0; }
Program iceroad; Var i,j,k,n,l,r,top,tail,ans:longint; a,f,g,q,p:array[0..400000] of longint; Begin readln(n,l,r); inc(n); for i:=1 to n do read(a[i]); top:=1; tail:=1; q[1]:=1; for i:=2 to n+r do begin j:=i-r; if j<0 then j:=0; k:=i-l; if k<0 then k:=0; while (top<=tail)and(i-r>q[top]) do inc(top); if (j<=q[top])and(q[top]<=k) then begin f[i]:=f[q[top]]+a[i]; g[i]:=q[top]; if f[i]>=f[ans] then ans:=i; end; while (top<=tail)and(f[k+1]>f[q[tail]]) do dec(tail); inc(tail); q[tail]:=k+1; end; writeln(f[ans]); i:=g[ans]; j:=0; while g[i]>0 do begin inc(j); p[j]:=g[i]-1; i:=g[i]; end; for j:=j downto 1 do write(p[j],' '); writeln('-1'); End.
Problem 4 上白泽慧音(classroom.cpp/c/pas)
题目描述 在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足
输入格式 第1行:两个正整数N,M
第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。
输出格式 第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。
输入样例 5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1
输出样例 3
1 3 5
数据范围 对于60%的数据:N <= 200且M <= 10,000
对于100%的数据:N <= 5,000且M <= 50,000
Classroom 上白泽慧音
题目大意 给定一张图,存在双向边和单向边若干,求出最大的强连通块。
考察算法 连通性
算法一 搜索。根据方法的不同能够得到不同的分值。
时间复杂度:O(?) 期望得分:0~100
算法二 通过Floyd判断连通性(F[i][j] = F[i][j] or (F[i][k] and F[k][j])),再将所有的双向连通点对统计出来,进行一次并查集。如果两个点是双向连通点对,把它们归为同一个集合。对于每个集合需要保存它的最小点编号。
时间复杂度:O(N^3) 期望得分:60
算法三 通过Tarjan算法求出图中所有的强连通分量,并记录每一个块的最小点编号。最后输出最大连通分量的顶点。
时间复杂度:O(N+M) 期望得分:100
#includeusing namespace std; #define MAXN 5009 #define MAXM 100009 int N, M; struct TARJAN { int v[ MAXM ], next[ MAXM ], fir[ MAXN ]; int dfn[ MAXN ], low[ MAXN ], stack[ MAXN ], tot[ MAXN ], mark[ MAXN ], rep[ MAXN ]; bool used[ MAXM ], instack[ MAXN ]; int cnt, top, ecnt, num; void DFS(int rt) { dfn[rt] = low[rt] = ++cnt; stack[ ++top ] = rt; instack[rt] = true; for (int p = fir[rt]; p; p = next[p]) if (!used[p]) { used[p] = true; if (!dfn[ v[p] ]) { DFS( v[p] ); low[rt] = min(low[rt], low[ v[p] ]); } else if (instack[ v[p] ]) low[rt] = min(low[rt], dfn[ v[p] ]); } if (dfn[rt] == low[rt]) { ++num; do { mark[ stack[ top ] ] = num; instack[ stack[ top-- ] ] = false; } while ( stack[ top + 1 ] != rt); } return ; } void Addedge(int rt, int s) { ++ecnt; v[ ecnt ] = s, next[ ecnt ] = fir[rt]; fir[ rt ]= ecnt; return ; } void Prin(int x) { bool first = true; printf("%d\n", tot[x]); for (int i = 1; i <= N; i++) if (mark[i] == x) { if (!first) printf(" "); first = false; printf("%d", i); } printf("\n"); return ; } void Work() { for (int i = 1; i <= N; i++) if (!dfn[i]) DFS(i); for (int i = 1; i <= N; i++) { tot[ mark[i] ]++; if (rep[ mark[i] ] == 0 || rep[ mark[i] ] > i) rep[ mark[i] ] = i; } int best = -1; for (int i = 1; i <= num; i++) if (best == -1 || (tot[i] > tot[ best ]) || (tot[i] == tot[ best ] && rep[i] < rep[ best ])) best = i; Prin( best ); return ; } } Tarjan; int main() { int u, v, t; scanf("%d %d", &N, &M); while (M--) { scanf("%d %d %d", &u, &v, &t); Tarjan.Addedge(u, v); if (t == 2) Tarjan.Addedge(v, u); } Tarjan.Work(); return 0; }
总体来说S1还是很弱的.