C - Mandarin Orange
提供一种不一样的 (mathcal{O}(n log n)) 的方法。
设对于第 (i) 个位置,左边第一个大于它的位置记为 (L_i) ,右边第一个大于它的位置记为 (R_i) 。
发现对于第 (i) 个位置的值作为 (x) 时的最优解即为 ((R_i-L_i-1) imes 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
", Ans);
return 0;
}
D - Logical Expression
简单递推题。
设 (T_i) ,(F_i) 分别表示前 (i) 个运算符计算结果的为 ( ext{True}) 或 ( ext{False}) 的数量。
如果第 (i) 个计算符为 AND
,则有
否则
答案即为 (T_n)
E - Rotate and Flip
这道题可以不用矩阵,也并不麻烦。
观察每个操作:
opt1
((x,y)
ightarrow (y,-x))
opt2
((x,y)
ightarrow (-y,x))
opt3
((x,y)
ightarrow (2 imes p-x,y))
opt4
((x,y)
ightarrow (x,2 imes p-y))
可以发现对于任何几种操作相加,对于原来的横纵坐标,都能表示成
((sum_x ext{opt}_x x, sum_y ext{opt}_y y)) 或 (( sum_y ext{opt}_y y,sum_x ext{opt}_x x))。
(sum) 表示一个值, ( ext{opt}) 表示正负号, (x/y) 表示原来横坐标或纵坐标的值
在具体操作时,只需要离线处理,维护 (sum_{x/y}) , ( ext{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
", Xans[i], Yans[i]);
return 0;
}
F - Sugoroku2
设 (f_i) 表示从第 (i) 个格子到终点的次数期望,显然有:
( egin{cases} f_i = displaystyle{0} & igeq n\ f_i = displaystyle{f_0} & iin A \ f_i = displaystyle{frac{1}{m}sum_{j=i+1}^{i+m} f_j}+1 & ext{otherwise}\ end{cases} )
这个式子不能直接求,但是每一个 (f_i) 可以表示成关于 (f_0) 的一次函数形式,最终为获得一个形如 (f_0 = k imes 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
", f1[0]/(1-f2[0]));
return 0;
}