感觉是有史以来打的最痛苦的一场VP
这罚时就离谱。
C题:
双端队列搞一下就行了。
#include <bits/stdc++.h>
using namespace std;
deque<int>sth;
int n, val;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d",&val);
if(i%2)
sth.push_back(val);
else
sth.push_front(val);
}
if(n % 2 == 0) {
while(!sth.empty()) {
printf("%d ",sth.front());
sth.pop_front();
}
return 0;
}
else {
while(!sth.empty()) {
printf("%d ",sth.back());
sth.pop_back();
}
return 0;
}
return 0;
}
D题:
考虑一下什么时候会重复,令
l
,
r
l,r
l,r分别为重复出现的元素的两个下标。
当且仅当,你在
[
1
,
l
−
1
]
[1,l-1]
[1,l−1]和
[
r
+
1
,
n
+
1
]
[r+1,n+1]
[r+1,n+1]里面各自选了些东西,且选了
l
,
r
l,r
l,r中的一个,且没有在
[
l
+
1
,
r
−
1
]
[l+1,r-1]
[l+1,r−1]中选东西时,会有重复。
容易得出
A
n
s
i
=
C
n
+
1
i
−
C
l
+
n
−
r
i
−
1
Ans_i=C_{n+1}^i-C_{l+n-r}^{i-1}
Ansi=Cn+1i−Cl+n−ri−1
注意这里实际上tm有n+1个元素,预处理阶乘的时候要多处理几个位置。
论Wa3发是怎么来的。以后预处理阶乘,统一多处理10个位子得了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1000000007;
long long jc[100010], inv[100010];
long long ans[100010];
int pre[100010];
int n, L, R;
long long fpow(long long di, long long top) {
long long ret = 1;
while(top) {
if(top % 2)
ret = ret * di % mod;
di = di * di % mod;
top /= 2;
}
return ret;
}
void init() {
jc[0] = 1;
for(int i=1;i<=100005;++i) {
jc[i] = jc[i-1] * i % mod;
}
inv[100005] = fpow(jc[100005], mod - 2);
for(int i=100005-1;i>=0;--i) {
inv[i] = inv[i+1] * (i+1) % mod;
}
jc[0] = inv[0] = 1;
return;
}
long long C(int n, int k) {
if(k > n)
return 0;
if(k == n)
return 1;
return jc[n] * inv[n - k] % mod * inv[k] % mod;
}
signed main() {
init();
int val;
scanf("%lld", &n);
for(int i=1;i<=n+1;++i) {
scanf("%lld", &val);
if(!pre[val])
pre[val] = i;
else
L = pre[val], R = i;
}
for(int i=1;i<=n+1;++i) {
ans[i] = C(n+1, i);
}
for(int i=1;i<=n+1;++i) {
ans[i] -= C(L + n - R, i - 1);
ans[i] %= mod, ans[i] += mod, ans[i] %= mod;
printf("%lld\n", ans[i]);
}
return 0;
}
E题:
好题。
考虑那个favorite按钮的本质,即,在我们想到到达一个点的时候,我们可以不用选择走路,而是直接花1的代价TP过去。
按照题意模拟一发,开个数组,
s
u
m
[
i
]
sum[i]
sum[i]表示,我们为了到达i点,花费的步数。
比如,我们当前在
3
3
3,想去
11
11
11,那么为了到
4
4
4,花费1步,为了到
5
5
5,花费2步,为了到
6
6
6,花费3步……
不难发现这个是个区间加等差数列。
然后发觉,按favorite也需要花一步诶。
那就把
s
u
m
[
i
]
sum[i]
sum[i]的含义更改为 “如果favorite在i,我们可以少走多少步”
每次做一个区间加等差数列就好。
两次前缀和。
由于是个环,先做一步拆环成链然后倍长。
前面几次wa是因为没有拆环,不知道为什么有三个点过不去。
#include <bits/stdc++.h>
using namespace std;
long long cha[400010];
long long ccha[400010];
long long sum[400010];
int a[400010];
int n, m;
int main() {
scanf("%d%d", &n, &m);
long long ans = 0, lasp;
for(int i=1;i<=n;++i) {
scanf("%d", &a[i]);
}
lasp = a[1];
for(int i=2;i<=n;++i) {
if(a[i] > lasp)
ans += a[i] - lasp;
else
ans += a[i] + m - lasp;
lasp = a[i];
}
// cout<<ans<<endl;
for(int i=2;i<=n;++i) {
int l = a[i-1] + 1, r = a[i];
if(l == m + 1)
l = 1;
// printf("!!! %d %d\n", l, r);
if(r >= l) {
ccha[l]++, ccha[r+1]--, cha[r+1] -= (r - l + 1);
cha[l] -= 1;
cha[r+1] += 1;
}
else {
int nl = l, nr = r + m;
ccha[nl]++, ccha[nr+1]--, cha[nr+1] -= (nr - nl + 1);
//
cha[nl]--, cha[nr+1]++;
}
}
for(int i=1;i<=m*2;++i)
ccha[i] += ccha[i-1];
for(int i=1;i<=m*2;++i)
cha[i] += ccha[i];
for(int i=1;i<=m*2;++i)
cha[i] += cha[i-1];
// for(int i=1;i<=m*2;++i)
// printf("%lld ",cha[i]);
// printf("\n");
for(int i=1;i<=m;++i)
sum[i] = cha[i] + cha[i + m];
long long mx = 0;
for(int i=1;i<=m;++i) {
mx = max(mx, sum[i]);
}
cout << ans - mx;
return 0;
}
赛中甚至没有时间看F题,我居然已经菜成这样了。今天的状态感觉拉的有点厉害qwq。
赛后看了眼F题,果然赛时没看好像没什么影响。
甚至连题解都没看懂。
坑在这里。
https://www.dazhuanlan.com/2019/12/30/5e094e2ed76cb/