Loading [MathJax]/jax/output/HTML-CSS/jax.js
  1. 1 雨き声残響 すぃ
  2. 2 ハレハレヤ Sou
雨き声残響 - すぃ
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

T1 小w的进制转换

w在将一个10进制数转换为二进制数的时候,不小心将“0”和“1”搞混了,也就是说本来该输出1的时候他输出了0,本来该输出0的时候他又输出了1。而他在输出答案的时候,又将输出的左右顺序搞混了。举个例子:小w6转换为二进制,本来6的二进制表示为110,但是小w转换成了001,输出时又倒了过来变成100。现在假设评测姬中的评测数据是1,2,3,4,5,6n也就是从1n,问小wAC其中的多少组测试案例?

这个东西是一个很纯的进制题,题目大意就是计数合法的串,满足各位异或1之后调转整个序列使得与原序列相同。也就是让你求“反对称01串”的个数。显然奇数长度的串是不会合法的,那么考虑偶数长度的串,推一推样例可以发现第N个符合长度的串就是N的二进制接上N的反对称串,熟悉位运算的话实际上就是一个模拟题,思维难度不大,就是看你能不能掌握位运算了。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define Half (Len >> 1)

using namespace std ;
typedef long long LL ;
const LL MAXN = 110 ;
const LL INF = 0x7fffffff ;
LL T, N, A[MAXN], Len, Old, Ans ;

inline LL Read() {
LL X = 0, F = 1 ; char ch = getchar() ;
while (ch > '9' || ch < '0') F = (ch == '-' ? - 1 : 1), ch = getchar() ;
while (ch >= '0' && ch <= '9') X=(X<<1)+(X<<3)+(ch^48), ch = getchar() ;
return X * F ;
}

int main() {
T = Read() ; while (T --) {
N = Read() ; Old = N ; Len = 0, Ans = 0 ;
while (Old) {
LL X = Old % 2 ;
if (X == 1) A[++ Len] = 1 ;
else A[++ Len] = 0 ;
Old >>= 1 ;
}
for (int i = 1 ; i <= Half ; i ++)
swap(A[i], A[Len - i + 1]) ;
if (Len & 1)
for (int i = 1 ; i <= Half ; i ++)
Ans += 1 << (i - 1) ;
else {
for (int i = 1 ; i <= (Half - 1) ; i ++)
Ans += 1 << i - 1 ;
for (int i = 2 ; i <= Half ; i ++)
if (A[i]) Ans += 1 << (Half - i) ;
Old = 0 ; LL Old2 = 0 ;
for (int i = 1 ; i <= Half ; i ++)
Old = (Old << 1) + A[i] ;
for (int i = Half ; i >= 1 ; i --)
Old2 = (Old2 << 1) - A[i] + 1 ;
if ((Old << Half) + Old2 <= N)
Ans ++ ;
}
printf("%lld\n", Ans) ;
}
return 0 ;
}

T2 小doge的快乐阳光跑

每次开始阳光跑,会在n个点里随机生成好多点位,并且需要按照点位顺序跑.

当且仅当,阳光跑随机生成的点位序列(注意顺序)是你跑步序列的一个子序列时,你完成阳光跑。

某一个序列的子序列指的是,保留该序列的顺序并删除一些元素或者不进行删除后的结果。

doge是一个乐于助人的好孩子,他常常帮助别人跑步。

这天,小doge想要完成自己的阳光跑并且顺便帮助小w一起完成即一个人完成两人的任务,并且两个人的任务是独立的,小dogea个点位,小wb个点位。小doge想要最效率的跑法,所以他想知道他最少要跑多远。小doge可以选择任何一个点出发。

对于阳光跑的要求,我们如下定义:有n个有顺序的点位a1an,想要完成阳光跑,必须按照顺序依次经过每个点位,并且在到达前一个点位后到达下一个点位才有效。

搬过来的题解里面的题意简化:给一张图,求一个权值和路径最小的移动序列,使得移动序列包含两个给定的子序列。(早这样说多好

这题很显然你应该求一个最短路,想用Floyed的大可不必了,数据范围对于AB来说只有100,所以我们可以直接对于每一个AB中的点为起点跑一遍SPFA,时间上是没有问题的。现在有了D[i][j]之后我们就可以进行Dp了,设Dp[i][j][0/1],表示当前完成了A的第i位和B的第j位,然后0为停留在A路径上,1为停留在B路径上。转移方程还是需要推一推的。

1
2
3
4
Dp[i][j][0] = min(Dp[i][j][0], Dp[i - 1][j][0] + D[La[i - 1]][La[i]]) ;
Dp[i][j][0] = min(Dp[i][j][0], Dp[i - 1][j][1] + D[Lb[j]][La[i]]) ;
Dp[i][j][1] = min(Dp[i][j][1], Dp[i][j - 1][1] + D[Lb[j - 1]][Lb[j]]) ;
Dp[i][j][1] = min(Dp[i][j][1], Dp[i][j - 1][0] + D[La[i]][Lb[j]]) ;

看起来非常的整齐(嗯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std ;
typedef long long LL ;
const LL MAXN = 1010 ;
const LL MAXM = 10010 ;
const LL MAXA = 110 ;
const LL INF = 0x7fffffff ;
LL N, M, H[MAXN], Tot, A, B, Dp[MAXA][MAXN][2] ;
LL La[MAXN], Lb[MAXN], D[MAXN][MAXN], Ans = 0 ;
struct Node {
LL F, T, L, Next ;
} E[MAXM << 1 << 1] ;
bool Vis[MAXN] ;

inline LL Read() {
LL X = 0, F = 1 ; char ch = getchar() ;
while (ch > '9' || ch < '0') F = (ch == '-' ? - 1 : 1), ch = getchar() ;
while (ch >= '0' && ch <= '9') X=(X<<1)+(X<<3)+(ch^48), ch = getchar() ;
return X * F ;
}

inline void Add(LL F, LL T, LL L) {
E[++ Tot].F = F ; E[Tot].T = T ; E[Tot].L = L ;
E[Tot].Next = H[F] ; H[F] = Tot ;
}

inline void Spfa(LL F) {
queue <LL> Q ;
memset(Vis, 0, sizeof (Vis)) ;
D[F][F] = 0 ; Vis[F] = 1 ; Q.push(F) ;
while (! Q.empty()) {
LL Now = Q.front() ; Q.pop() ; Vis[Now] = 0 ;
for (LL i = H[Now] ; i ; i = E[i].Next)
if (D[F][E[i].T] > D[F][Now] + E[i].L) {
D[F][E[i].T] = D[F][Now] + E[i].L ;
if (! Vis[E[i].T])
Q.push(E[i].T), Vis[E[i].T] = 1 ;
}
}
}

int main() {
N = Read() ; M = Read() ;
for (LL i = 1 ; i <= M ; i ++) {
LL X = Read(), Y = Read(), Z = Read() ;
Add(X, Y, Z) ; Add(Y, X, Z) ;
}
memset(D, 127, sizeof (D)) ;
A = Read() ;
for (LL i = 1 ; i <= A ; i ++) La[i] = Read(), Spfa(La[i]) ;
B = Read() ;
for (LL i = 1 ; i <= B ; i ++) Lb[i] = Read(), Spfa(Lb[i]) ;
memset(Dp, 127, sizeof (Dp)) ;
Dp[0][0][0] = Dp[0][0][1] = 0 ;
for (LL i = 1 ; i <= N ; i ++) D[0][i] = 0 ;
for (LL i = 1 ; i <= A ; i ++)
Dp[i][0][0] = Dp[i - 1][0][0] + D[La[i - 1]][La[i]] ;
for (LL i = 1 ; i <= B ; i ++)
Dp[0][i][1] = Dp[0][i - 1][1] + D[Lb[i - 1]][Lb[i]] ;
for (LL i = 1 ; i <= A ; i ++)
for (LL j = 1 ; j <= B ; j ++) {
Dp[i][j][0] = min(Dp[i][j][0], Dp[i - 1][j][0] + D[La[i - 1]][La[i]]) ;
Dp[i][j][0] = min(Dp[i][j][0], Dp[i - 1][j][1] + D[Lb[j]][La[i]]) ;
Dp[i][j][1] = min(Dp[i][j][1], Dp[i][j - 1][1] + D[Lb[j - 1]][Lb[j]]) ;
Dp[i][j][1] = min(Dp[i][j][1], Dp[i][j - 1][0] + D[La[i]][Lb[j]]) ;
}
printf("%lld\n", min(Dp[A][B][0], Dp[A][B][1])) ;
return 0 ;
}

T3 区间异或和异或区间最大值异或区间最小值

w学会了RMQ算法,他现在可以求出一个给定数组某一段子区间的最大值,最小值。在这之前,他也学会了前缀和,并且他知道前缀和可以扩展到位运算求出区间异或和。 现在你给了他一个长度大小为n的数组,为了考察小wRMQ以及前缀异或和的正确性,你要求他求出该数组的某一个子区间,记该子区间的异或和为xorsum,记该子区间的最大值为max,记该子区间的最小值为min,你要求使得xorsummaxmin最大。其中为位运算异或操作。

看起来就是一道很毒瘤的数据结构题,(从题目名字就看得出来好嘛

先考虑考虑部分分吧。首先我们得知道一个原理ABB=A,相当于一个数两次异或另一个数答案是不变的。那么对于答案所求就是去掉最大值和最小值之后的区间异或和。对于区间最大值和区间最小值我们可以使用RMQ进行维护,对于Sumxor我们可以使用前缀和进行维护,于是对于确定区间的求值就只O(1)的,我们枚举区间的左右端点,时间复杂度就是O(N2)的。

但是出题人十分毒瘤,导致O(N2)只能过30%的点,这就有些不良心了。。

接下来想正解,发现这个序列实际上是可以用分治瞎搞进行维护的,对于该区间取中点Mid,然后考虑从左端点在Mid左侧,右端点在Mid右侧的所有子区间,它们有一个共性就是一定是由一个从Mid开始往左延伸的区间和从Mid开始往右延伸的区间拼接得到的(意会),对于这两个区间我们需要找到一个支持快速插入以及查询的数据结构来进行维护从Mid向左延伸的区间的贡献,之后再遍历往右的区间。

这样我们就处理完了所有左端点在Mid左侧,右端点在Mid右侧的子区间,那么之后直接递归计算[L,Mid][Mid+1,R]就可以了。

回想这个数据结构,我们发现字典树可以较好地解决这个问题,我们只需要将异或和放入字典树中,然后分类讨论:

  1. 左侧区间提供最大值最小值
  2. 右侧区间提供最大值最小值
  3. 左侧区间提供最大值,右侧区间提供最小值
  4. 左侧区间提供最小值,右侧区间提供最大值

并且,由于显而易见的是随着区间延伸最大值单调不减,最小值单调不升,所以我们枚举极值的时候有一个双单调性,因此可以考虑尺取法,之中对于以上四种情况分类讨论即可。

思维难度颇大,鄙人在考场上大致也只能想出来O(N2)暴力做法。(没准还可以水一水特殊数据点?

本文标题:

文章作者:Sue Shallow

发布时间:2019年09月27日 - 21:30:00

最后更新:2019年11月11日 - 19:52:34

原始链接:http://Yeasion.github.io/2019/09/27/Contest Nowcoder1090 - TG Months 12/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Powered By Valine
v1.5.2