题解: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_i = T_{i-1} \]

\[F_i = F_{i-1}\times 2 + T_{i-1} \]

否则

\[F_i = F_{i-1} \]

\[T_i = T_{i-1}\times 2 + F_{i-1} \]

答案即为 \(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;
}
posted @ 2021-01-24 11:36  AxDea  阅读(183)  评论(0编辑  收藏  举报