这场依旧质量很高,题目都有一种耳目一新的感觉。
A. Array and Peaks
题意: 一个下标
i
i
i被成为peak的条件是
1
<
i
<
n
,
a
i
>
a
i
−
1
,
a
i
>
a
i
+
1
1<i<n,a_i>a_{i-1},a_i>a_{i+1}
1<i<n,ai>ai−1,ai>ai+1。现给出一个数字
n
n
n,要求构造一个peak数恰为
k
k
k的排列。如果不能构造,输出-1。
做法: 瞎搞。首先可以断定,
2
k
+
1
>
n
2k+1>n
2k+1>n时,无法构造。否则,我们仅使用
1
−
2
k
+
1
1-2k+1
1−2k+1就能构造出来。将
1
−
k
+
1
1-k+1
1−k+1放到前面的奇数位置,将
k
+
2
−
2
k
k+2-2k
k+2−2k放到偶数位置上,之后剩下的位置
a
i
=
i
a_i=i
ai=i即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, k, a[120];
int main()
{
ios::sync_with_stdio(false);
int T;
cin >> T;
while(T--)
{
cin >> n >> k;
if(k * 2 >= n) cout << -1 << endl;
else
{
for(int p = 0, i = 1; p <= k; p++, i++) a[2 * p + 1] = i;
for(int p = 1, j = k + 2; p <= k; p++, j++) a[2 * p] = j;
for(int i = 2 * k + 2; i <= n; i++) a[i] = i;
for(int i = 1; i <= n; i++)
{
cout << a[i];
if(i == n) cout << endl;
else cout << ' ';
}
}
}
return 0;
}
B. AND Sequences
题意: AND序列的定义是对于所有的
<
n
<n
<n的
i
i
i,都有
A
N
D
j
=
1
i
=
A
N
D
j
=
i
+
1
n
AND_{j=1}^{i}=AND_{j=i+1}^{n}
ANDj=1i=ANDj=i+1n。现在给出一个数组
a
a
a,你可以打乱其下标的顺序形成一个新的数组,问由多少下标序列能够使得打乱后的数组为AND序列?
做法: 首先,AND序列的条件还是比较有规律的,我们试试能从题目条件中推导出什么不。首先
a
1
=
A
N
D
j
=
2
n
a_1=AND_{j=2}^{n}
a1=ANDj=2n,
a
1
&
a
2
=
A
N
D
j
=
2
n
&
a
2
=
A
N
D
j
=
3
n
a_1\&a_2=AND_{j=2}^{n}\&a_2=AND_{j=3}^{n}
a1&a2=ANDj=2n&a2=ANDj=3n,说明
a
2
&
A
N
D
j
=
3
n
=
A
N
D
j
=
3
n
a2\&AND_{j=3}^{n}=AND_{j=3}^{n}
a2&ANDj=3n=ANDj=3n,以此类推,最终可以得到
a
n
−
1
&
a
n
=
a
n
a_{n-1}\&a_n=a_n
an−1&an=an,再将这个结果依次回代,最终可以得到
a
i
&
a
n
=
a
n
a_i\&a_n=a_n
ai&an=an。说明,一个AND序列里的最后一个元素是最小的。另外,我们回代到
a
1
=
A
N
D
j
=
2
n
=
a
n
a_1=AND_{j=2}^{n}=a_n
a1=ANDj=2n=an时可以发现,
a
1
a_1
a1和
a
n
a_n
an的值相等。至此,可以发现,一个AND序列的两头都是最小值,并且其他任何一个元素与最小值做按位与都是最小值本身。
所以,我们首先检查
a
a
a数组中的每个元素是否满足和最小值按位与还是等于最小值的条件。顺便统计一下最小值的个数,设为
m
m
m,答案就是
m
∗
(
m
−
1
)
∗
(
n
−
1
)
!
m*(m-1)*(n-1)!
m∗(m−1)∗(n−1)!。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, a[N], f;
ll fac[N], ans;
map<int, int> mp;
int main()
{
ios::sync_with_stdio(false);
fac[0] = 1;
for(int i = 1; i <= 2e5; i++) fac[i] = fac[i - 1] * i % mod;
int T;
cin >> T;
while(T--)
{
cin >> n;
mp.clear();
f = 1;
for(int i = 1; i <= n; i++) cin >> a[i], mp[a[i]]++;
sort(a + 1, a + 1 + n);
for(int i = 1; i <= n; i++)
{
if((a[i] & a[1]) != a[1])
{
f = 0;
break;
}
}
if(!f) cout << 0 << endl;
else
{
ans = fac[n - 2] * mp[a[1]] % mod * (mp[a[1]] - 1) % mod;
cout << ans << endl;
}
}
return 0;
}
C. Add One
题意: 定义一个+1操作,规则如下:将
n
n
n的十进制每一位都+1,结果字符串拼起来就是操作结果,如19执行完变成了210。现在给出
n
,
m
n,m
n,m,求
n
n
n操作
m
m
m次后结果由多少位。
做法(官方题解): 我们注意到数字片段10是比较特殊的,因为任何一个数字位要产生新的数位,都必须经过它,到达了10后演变路线完全一致。所以我们来考虑
10
10
10操作
m
m
m次后能够生成多少数位。设
d
p
[
j
]
dp[j]
dp[j]表示数字
10
10
10操作了
m
m
m次后产生的位数。显然,
10
10
10要产生新的位至少操作9次,因此有
d
p
[
i
]
+
=
d
p
[
i
−
9
]
dp[i]+=dp[i-9]
dp[i]+=dp[i−9],10操作了9次后变为109,前面的10部分产生的位数反映在了
d
p
[
i
−
9
]
dp[i-9]
dp[i−9]里。还有一个数字9,此时再操作一次还会产生新的进位2110,因而有
d
p
[
i
]
+
=
d
p
[
i
−
10
]
dp[i]+=dp[i-10]
dp[i]+=dp[i−10],这里就将数字9的贡献考虑了进来。总的来说,
d
p
[
i
]
=
d
p
[
i
−
9
]
+
d
p
[
i
−
10
]
dp[i]=dp[i-9]+dp[i-10]
dp[i]=dp[i−9]+dp[i−10],递推即可,初值也很好设置
d
p
[
k
]
=
2
,
k
<
9
,
d
p
[
9
]
=
3
dp[k]=2,k<9,dp[9]=3
dp[k]=2,k<9,dp[9]=3。
对于每个数位,我们先将其凑出10,然后计算剩下的次数给10用能够产生多少新的数位。注意有些数位操作
m
m
m次后是凑不出10的,此时只对答案有1位的贡献。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 1e9 + 7;
ll ans, dp[N], m;
char s[N];
int main()
{
ios::sync_with_stdio(false);
for(int i = 0; i < 9; i++) dp[i] = 2;
dp[9] = 3;
for(int i = 10; i <= 2e5; i++) dp[i] = (dp[i - 9] + dp[i - 10]) % mod;
int T;
cin >> T;
while(T--)
{
cin >> s + 1 >> m;
ans = 0;
for(int i = 1; s[i]; i++)
{
int n = s[i] - '0';
ans += (m - 10 + n) < 0? 1 : dp[m + n - 10];
ans %= mod;
}
cout << ans << endl;
}
return 0;
}
做法2(记忆化搜索,感谢队友的提醒): 题目中给出的操作显然是各数位之前毫无影响的,因此我们分别计算每个数位上的数字演变
m
m
m次可以产生多少新的数位。
d
p
[
d
]
[
j
]
dp[d][j]
dp[d][j]表示数位上是
d
d
d操作
j
j
j次可以生成的位数。转移很简单:
d
p
[
d
]
[
j
]
=
d
p
[
1
]
[
j
−
1
]
+
d
p
[
0
]
[
j
−
1
]
,
d
=
9
,
d
p
[
d
]
[
j
]
=
d
p
[
d
+
1
]
[
j
−
1
]
,
d
≠
9
dp[d][j]=dp[1][j-1]+dp[0][j-1],d=9,dp[d][j]=dp[d+1][j-1],d\neq 9
dp[d][j]=dp[1][j−1]+dp[0][j−1],d=9,dp[d][j]=dp[d+1][j−1],d=9。
初值
d
p
[
0
−
9
]
[
0
]
=
1
dp[0-9][0]=1
dp[0−9][0]=1。
然后就是记忆化搜索并计算答案了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 1e9 + 7;
ll dp[10][N], ans, m;
char s[N];
ll dfs(int i, int j)
{
if(dp[i][j] != -1) return dp[i][j];
ll res = 0;
if(i == 9) res = (dfs(1, j - 1) + dfs(0, j - 1)) % mod;
else res = dfs(i + 1, j - 1);
return dp[i][j] = res;
}
int main()
{
ios::sync_with_stdio(false);
memset(dp, -1, sizeof(dp));
for(int i = 0; i <= 9; i++) dp[i][0] = 1;
int T;
cin >> T;
while(T--)
{
ans = 0;
cin >> s + 1 >> m;
for(int i = 1; s[i]; i++)
{
ans += dfs(s[i] - '0', m);
ans %= mod;
}
cout << ans << endl;
}
return 0;
}