题解:AtCoder Beginner Contest 189
C - Mandarin Orange
提供一种不一样的 \(\mathcal{O}(n \log n)\) 的方法。
设对于第 \(i\) 个位置,左边第一个大于它的位置记为 \(L_i\) ,右边第一个大于它的位置记为 \(R_i\) 。
发现对于第 \(i\) 个位置的值作为 \(x\) 时的最优解即为 \((R_i-L_i-1)\times val_i\) 。
可以使用双向链表处理,具体操作见代码。
int n, arr[N], id[N], Nxt[N], Pre[N];
inline bool cmp(int A, int B) {return arr[A] == arr[B] ? A < B : arr[A] > arr[B];}
int main() {
scanf("%d", &n);
forn(i,1,n) scanf("%d", arr+i), Pre[i] = i-1, Nxt[i] = i+1, id[i] = i;
int Ans = 0;
sort(id+1, id+n+1, cmp);
forn(i,1,n) {
int l = Pre[id[i]], r = Nxt[id[i]];
Nxt[l] = r,Pre[r] = l;
Ans = Max(Ans, (r-l-1) * arr[id[i]]);
}
printf("%d\n", Ans);
return 0;
}
D - Logical Expression
简单递推题。
设 \(T_i\) ,\(F_i\) 分别表示前 \(i\) 个运算符计算结果的为 \(\text{True}\) 或 \(\text{False}\) 的数量。
如果第 \(i\) 个计算符为 AND
,则有
否则
答案即为 \(T_n\)
E - Rotate and Flip
这道题可以不用矩阵,也并不麻烦。
观察每个操作:
opt1
\((x,y) \rightarrow (y,-x)\)
opt2
\((x,y) \rightarrow (-y,x)\)
opt3
\((x,y) \rightarrow (2\times p-x,y)\)
opt4
\((x,y) \rightarrow (x,2\times p-y)\)
可以发现对于任何几种操作相加,对于原来的横纵坐标,都能表示成
\((sum_x\ \text{opt}_x\ x, sum_y \ \text{opt}_y\ y)\) 或 \(( sum_y \ \text{opt}_y\ y,sum_x\ \text{opt}_x\ x)\)。
\(sum\) 表示一个值, \(\text{opt}\) 表示正负号, \(x/y\) 表示原来横坐标或纵坐标的值
在具体操作时,只需要离线处理,维护 \(sum_{x/y}\) , \(\text{opt}_{x/y}\) , 是否翻转横纵坐标, 5 个变量即可。
inline bool cmp(int NA, int NB) {return A[NA] < A[NB];}
int main() {
scanf("%d", &n);
forn(i,1,n) scanf("%d%d", X+i, Y+i);
scanf("%d", &m);
forn(i,1,m) {
scanf("%d", opt+i);
if(opt[i] > 2) scanf("%d", p+i);
}
scanf("%d", &q);
forn(i,1,q) scanf("%d%d", A+i, B+i), id[i] = i;
sort(id+1, id+q+1, cmp);
LL trn = 0, Xopt = 1, Yopt = 1, Xsum = 0, Ysum = 0;
int j = 1;
forn(i,1,m+1) {
while(A[id[j]] == i-1) {
Xans[id[j]] = Xsum + Xopt*1ll*X[B[id[j]]];
Yans[id[j]] = Ysum + Yopt*1ll*Y[B[id[j]]];
if(trn) swap(Xans[id[j]], Yans[id[j]]);
++j;
}
if(opt[i] == 1) {
if(trn) {
Yopt = -Yopt;
Ysum = -Ysum;
} else {
Xopt = -Xopt;
Xsum = -Xsum;
}
trn^=1;
} else if(opt[i] == 2) {
if(trn) {
Xopt = -Xopt;
Xsum = -Xsum;
} else {
Yopt = -Yopt;
Ysum = -Ysum;
}
trn^=1;
} else if(opt[i] == 3) {
if(trn) {
Ysum = 2ll*p[i] - Ysum;
Yopt = -Yopt;
} else {
Xsum = 2ll*p[i] - Xsum;
Xopt = -Xopt;
}
} else {
if(trn) {
Xsum = 2ll*p[i] - Xsum;
Xopt = -Xopt;
} else {
Ysum = 2ll*p[i] - Ysum;
Yopt = -Yopt;
}
}
}
forn(i,1,q) printf("%lld %lld\n", Xans[i], Yans[i]);
return 0;
}
F - Sugoroku2
设 \(f_i\) 表示从第 \(i\) 个格子到终点的次数期望,显然有:
\( \begin{cases} \ f_i = \displaystyle{0} & i\geq n\\ \ f_i = \displaystyle{f_0} & i\in A \\ \ f_i = \displaystyle{\frac{1}{m}\sum_{j=i+1}^{i+m} f_j}+1 & \text{otherwise}\\ \end{cases} \)
这个式子不能直接求,但是每一个 \(f_i\) 可以表示成关于 \(f_0\) 的一次函数形式,最终为获得一个形如 \(f_0 = k\times f_0+b\) 的式子,直接解方程即可。
所以可以用后缀和优化的 DP 来求出每一个一次函数,总复杂度仅为 \(\mathcal{O}(n)\) 。
另附本题加强版
#include<bits/stdc++.h>
#define forn(i,s,t) for(register int i=(s);i<=(t);++i)
#define form(i,s,t) for(register int i=(s);i>=(t);--i)
using namespace std;
const int N = 2e5+3;
int n, m, k, a[N]; bool bck[N];
long double f1[N], f2[N], suf1[N], suf2[N];
int main() {
scanf("%d%d%d", &n, &m, &k);
long double C = (long double)1.0/m;
forn(i,1,k) scanf("%d", a+i), bck[a[i]] = 1;
form(i,n-1,0) {
if(bck[i]) {
suf1[i] = suf1[i+1];
suf2[i] = suf2[i+1] + 1.0;
continue ;
}
f1[i] = C*(suf1[i+1] - suf1[i+m+1]) + 1;
f2[i] = C*(suf2[i+1] - suf2[i+m+1]);
suf1[i] = suf1[i+1] + f1[i];
suf2[i] = suf2[i+1] + f2[i];
}
if(1-f2[0] < 1e-6) puts("-1");
else printf("%.4Lf\n", f1[0]/(1-f2[0]));
return 0;
}