2020牛客暑期多校训练营(第六场)

比赛链接

收获

  • 给定一个排列 $p_i$,从 $i$ 向 $p_i$ 连边可以形成若干环。和排列相关的题可以这样想一下。

A African Sort

留坑,待补

B Binary Vector

每次加入的向量都不属于之前的向量空间,故 $f_n=\prod\limits_{i=0}^{n-1}\left(1-\frac{2^i}{2^n}\right)=\frac{\prod\limits_{i=0}^{n-1}(2^n-2^i)}{2^{n^2}}$。易得递推式:$f_n=f_{n-1}\cdot\frac{2^n-1}{2^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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const LL MOD = 1e9 + 7;
const LL inv2 = 500000004;
const int N = 2e7+5;

int T, n;
LL f[N], inv = inv2;

int main(){
f[1] = 5e8+4; for(int i = 2; i <= 2e7; i++){
(inv *= inv2) %= MOD;
f[i] = (f[i-1] * (1 - inv) % MOD + MOD) % MOD;
}
for(int i = 2; i <= 2e7; i++) f[i] ^= f[i-1];
for(read(T); T; T--){
read(n);
printf("%lld\n", f[n]);
}
return 0;
}

C Combination of Physics and Maths

又做麻烦了……

首先,如果选出的矩形有多列,那么一定可以把它分开,分出的一部分答案不会更差。证明:设 $\frac{a}{b}\leqslant\frac{c}{d}$,那么 $\frac{a}{b}\leqslant\frac{a+c}{b+d}\leqslant\frac{c}{d}$。所以答案是一列。

枚举这一列的最下面的数字,把它上面的所有数字都选上,更新答案。

复杂度:$O(Tnm)$

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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 205;

int T, n, m;
LL a[N][N], colSum[N][N];

int main(){
for(read(T); T; T--){
read(n, m);
for(int j = 1; j <= m; j++) colSum[0][j] = 0;
double ans = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
read(a[i][j]);
colSum[i][j] = colSum[i-1][j] + a[i][j];
ans = max(ans, 1.0 * colSum[i][j] / a[i][j]);
}
}
printf("%.8f\n", ans);
}
return 0;
}

E Easy Construction

$n$ 个数的和要满足条件,所以 $\frac{n(n+1)}{2}\bmod n=k$。

  • 当 $n$ 是奇数时,一定有 $k=0$,所以构造 $\{n,1,n-1,2,n-2,\dots\}$;
  • 当 $n$ 是偶数时,$\frac{n(n+1)}{2}\equiv k\pmod n\iff n(n+1)\equiv 2k\pmod n\iff k=\frac{n}{2}$,所以构造 $\left\{n,\frac{n}{2},1,n-1,2,n-2,\cdots\right\}$。
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int n, k;

int main(){
read(n, k);
if(n * (n + 1) / 2 % n != k) return puts("-1"), 0;
if(n & 1){
printf("%d ", n);
for(int i = 1; i <= n / 2; i++) printf("%d %d ", i, n - i);
puts("");
}
else{
printf("%d %d ", n, n / 2);
for(int i = 1; i < n / 2; i++) printf("%d %d ", i, n - i);
puts("");
}
return 0;
}

G Grid Coloring

$n=1,k=1,k\nmid 2n(n+1)$ 的情况无解,其余情况可以构造。

一种简单的构造方法是:轮换着用颜色按顺序涂水平边和竖直边。这样做相当于把格子涂成了阶梯状。

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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T, n, k;

int main(){
for(read(T); T; T--){
read(n, k);
if(2 * n * (n + 1) % k){ puts("-1"); continue; }
if(n == 1 || k == 1){ puts("-1"); continue; }
int now = 0;
vi vert;
for(int i = 1; i <= n + 1; i++){
for(int j = 1; j <= n; j++){
now = now % k + 1;
printf("%d ", now);
}
puts("");
if(i <= n){
for(int j = 1; j <= n + 1; j++){
now = now % k + 1;
vert.pb(now);
}
}
}
for(int i = 0; i <= n; i++){
for(int j = i; j < n * (n + 1); j += n + 1)
printf("%d ", vert[j]);
puts("");
}
}
return 0;
}

H Harmony Pairs

一个无比暴力的数位 $\text{dp}$。

设 $dp[i,j,0/1,0/1/2]$ 表示“从高到低考虑前 $i$ 位数,$S(A)$ 和 $S(B)$ 之差为 $j$,$B$ 是否顶住上界,$A$ 和 $B$ 的关系是什么”的数有多少,然后暴力枚举 $A$ 和 $B$ 第 $i$ 位的数字进行转移。

复杂度:$O(n^2\cdot 10^3)$

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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const LL MOD = 1e9 + 7;

int n;
char s[105];
LL dp[105][2005][2][3];

int main(){
scanf("%s", s+1);
n = strlen(s+1);
dp[0][1000][1][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = -900; j <= 900; j++){
for(int A = 0; A <= 9; A++){
for(int B = 0; B <= 9; B++){
int diff = j + A - B;
if(diff < -900 || diff > 900) continue;
if(A == B) (dp[i][diff+1000][0][0] += dp[i-1][j+1000][0][0]) %= MOD;
else if(A < B) (dp[i][diff+1000][0][1] += dp[i-1][j+1000][0][0]) %= MOD;
else (dp[i][diff+1000][0][2] += dp[i-1][j+1000][0][0]) %= MOD;
(dp[i][diff+1000][0][1] += dp[i-1][j+1000][0][1]) %= MOD;
(dp[i][diff+1000][0][2] += dp[i-1][j+1000][0][2]) %= MOD;

if(B > (s[i]-'0')) continue;
if(B == s[i]-'0'){
if(A == B) (dp[i][diff+1000][1][0] += dp[i-1][j+1000][1][0]) %= MOD;
else if(A < B) (dp[i][diff+1000][1][1] += dp[i-1][j+1000][1][0]) %= MOD;
(dp[i][diff+1000][1][1] += dp[i-1][j+1000][1][1]) %= MOD;
}
else{
if(A == B) (dp[i][diff+1000][0][0] += dp[i-1][j+1000][1][0]) %= MOD;
else if(A < B) (dp[i][diff+1000][0][1] += dp[i-1][j+1000][1][0]) %= MOD;
else (dp[i][diff+1000][0][2] += dp[i-1][j+1000][1][0]) %= MOD;
(dp[i][diff+1000][0][1] += dp[i-1][j+1000][1][1]) %= MOD;
(dp[i][diff+1000][0][2] += dp[i-1][j+1000][1][2]) %= MOD;
}
}
}
}
}
LL res = 0;
for(int d = 1; d <= 900; d++){
res += dp[n][d+1000][0][0] + dp[n][d+1000][0][1] + dp[n][d+1000][1][0] + dp[n][d+1000][1][1];
res %= MOD;
}
printf("%lld\n", res);
return 0;
}

J Josephus Transform

这道题的关键在与求出一次 $k$ 约瑟夫变换的置换数组,这之后置换 $x$ 次可以快速幂完成。

设上一次取出的数当时从左往右数第 $p$ 个,现在还剩 $c$ 个数,那么这次取出的数是从 $p$ 开始数 $k$ 个,对 $c$ 取模,即第 $(p+k-1-1)\bmod c+1$ 个数。可以用线段树完成。

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
75
76
77
78
79
80
81
82
83
84
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 100005;

int n, m, k, x;

struct segTree{
int l, r, sz;
}tr[N<<2];
#define lid id<<1
#define rid id<<1|1
#define mid ((tr[id].l + tr[id].r) >> 1)
inline void pushup(int id){
tr[id].sz = tr[lid].sz + tr[rid].sz;
}
void build(int id, int l, int r){
tr[id].l = l, tr[id].r = r;
if(l == r){ tr[id].sz = 1; return; }
build(lid, l, mid), build(rid, mid+1, r);
pushup(id);
}
void del(int id, int pos){
if(tr[id].l == tr[id].r){ tr[id].sz--; return; }
if(pos <= mid) del(lid, pos);
else del(rid, pos);
pushup(id);
}
int queryKth(int id, int k){
if(tr[id].l == tr[id].r) return tr[id].l;
if(tr[lid].sz >= k) return queryKth(lid, k);
else return queryKth(rid, k - tr[lid].sz);
}

vi get(){
vi res(n+1);
int p = 1, c = n;
build(1, 1, n);
for(int i = 1; i <= n; i++){
res[i] = queryKth(1, (p + k - 2) % c + 1);
del(1, res[i]);
p = (p + k - 2) % c + 1, c--;
}
return res;
}

inline vi trans(vi a, vi b){
vi res(n+1);
for(int i = 1; i <= n; i++) res[i] = a[b[i]];
return res;
}

inline void fpow(vi &res, vi bs, int idx){
while(idx){
if(idx & 1) res = trans(res, bs);
bs = trans(bs, bs);
idx >>= 1;
}
}

int main(){
read(n, m);
vi a(n+1);
for(int i = 1; i <= n; i++) a[i] = i;
while(m--){
read(k, x);
vi t = get();
fpow(a, t, x);
}
for(int i = 1; i <= n; i++) printf("%d ", a[i]);
puts("");
return 0;
}

K K-Bag

我的做法很玄学,虽然过了,但是不知道正确性到底咋样……

我先遍历所有数,对每个相同的数从小到大进行编号,但是编号不小于已经编过的号。举例说明:$\{2,3,2,1,3,3,2,1\}$ 的编号是 $1,1,2,2,2,3,3,3$,其中,第三个数字 $2$ 是第二次出现的,所以编号为 $2$ ,第四个数字 $1$ 虽然是第一次出现,但是我们已经有编号为 $2$ 的数了,所以从 $2$ 开始编。其实这样做本质就是把序列给划分了,为判断是否是一个合法的划分,只需要判断除了首尾两段以外,其他编号相同的连续一段长度是否等于 $k$。

但是这样做完会被 $\text{Hack}$,例如:$\{2,3,1,3,2,3,2,1,3,2\}$ 的编号是 $1,1,1,2,2,3,3,3,4,4$,编号为 $2$ 的长度只有 $2$,会被判断成 NO。究其原因,是因为第三个数 $1$ 本应被划分到后面的段(即本应编为 $2$),却在这里被往前划了。未解决这个问题,我就……呃……从后往前修正一下编号,比如这个 $1$ 的下一个 $1$ 编号是 $3$,所以我就把它编号改成 $3-1=2$,然后就过了……(虽然看上去很不对的样纸)

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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const int N = 500005;

int T, n, k, a[N], tag[N], t[N];

bool check(){
for(int i = 1; i <= n; i++){
int j = i;
while(j < n && tag[j+1] == tag[i]) j++;
if(j - i + 1 != k)
if(i != 1 && j != n)
return false;
i = j;
}
return true;
}

int main(){
for(read(T); T; T--){
read(n, k);
for(int i = 1; i <= n; i++){
read(a[i]);
t[i] = a[i];
tag[i] = 0;
}
sort(t+1, t+n+1);
int len = unique(t+1, t+n+1) - (t+1);
for(int i = 1; i <= n; i++)
a[i] = lower_bound(t+1, t+len+1, a[i]) - t;

int mxtag = 0;
vi pre(n+5);
bool ok = true;
for(int i = 1; i <= n; i++){
if(a[i] > k || a[i] < 1){
ok = false;
break;
}
tag[i] = max(mxtag, pre[a[i]] + 1);
pre[a[i]] = tag[i];
mxtag = max(mxtag, tag[i]);
}
if(!ok){ puts("NO"); continue; }

ok = check();
if(ok){ puts("YES"); continue; }

vi suc(n+5);
for(int i = n; i >= 1; i--){
if(!suc[a[i]]) suc[a[i]] = tag[i];
else tag[i] = --suc[a[i]];
}
ok = check();
if(ok){ puts("YES"); continue; }
else puts("NO");
}
return 0;
}
作者

xyfJASON

发布于

2020-07-27

更新于

2021-08-28

许可协议

评论