20180805 提高Day2 模拟赛
题目目录
主席树
题意
给定序列 $A$ ,你需要将 $A$ 中所有长度 $\geq k$ 的子区间中每一个子区间的第 $\mathbf{k}$ 小的元素提出来组成序列 $B$,然后输出 $B$ 序列中的第 $\mathbf{l}$ 大的元素。$n \leq 100000, A_i \leq 10^8$ 。
题解
这道题和这道题有相同之处。
我们考虑二分答案 $x$ ,将 $A$ 中所有 $\leq x$ 的元素记为 $0$ ,其他记为 $1$ 。
然后,我们用双指针统计被放到 $B$ 中的 $0$ 的个数——如果一个合法区间里有 $\geq k$ 个 $0$ ,那么它会对答案贡献一个 $0$ 。
调试记录
- 双指针开始的时候向右偏移的元素多了一个
- 二分写错了。。。
代码
#include <bits/stdc++.h> using namespace std; #define fin cin #define fout cout typedef long long ll; #define int long long int n, k, l, b[100005], A[100005]; struct node { int v, id; bool operator< (const node&res)const { return v < res.v; } } a[100005]; bool check(int x, ll tot) { int pre = 0; for (int i=1; i<=n; i++) if (A[i] <= x) b[pre] = i, pre = i; b[pre] = n+1; ll ret = 0, lst = 0, cur = b[0], r=cur; for (int i=1; i<k && r!=n+1; i++) r = b[r]; if (r == n+1) { return tot < ::l; } do { ret += 1ll * (cur-lst) * (n-r+1); lst = cur; cur = b[cur]; r = b[r]; } while (r != n+1); return tot - ret < l; } signed main() { ios :: sync_with_stdio(false); fin >> n >> k >> l; for (int i=1; i<=n; i++) fin >> a[i].v, a[i].id = i; sort(a+1, a+1+n); int m=1; A[a[1].id]=1; for (int i=2; i<=n; i++) { if (a[i].v != a[i-1].v) ++m; A[a[i].id] = m; } int l=1, r=m; ll tot = 1ll*(n-k+2)*(n-k+1); tot>>=1ll; while (l<r) { int mid = l + r >> 1; bool ret = check(mid, tot); if (check(mid, tot)) r=mid; else l=mid+1; } a[0].v = -1e9; int now = 0; for (int i=1; i<=n; i++) if (a[i].v != a[i-1].v) { if (++now == l) { fout << a[i].v << endl; return 0; } } return 233; }
决斗
题意
两个人,每个人有血量。每个人攻击时会对对方等概率地产生 $l$ 到 $r$ 的伤害(伤害为整数)。
两个人的 $l,r$ 不同。第一个人先攻击,求第一个人获胜的概率。
题解
$f_{i,j,k}$ 表示当前状态下第一个人剩余血量为 $i$,第二个人剩余血量为 $j$ ,当前先手为 $k$ 的概率。
$k=0$:$f_{i,j,k}=\left(f_{i+l2,j,k^1}+f_{i+l2+1,j,k^1}+\cdots+f_{i+r2,j,k^1}\right)/\left(r_2-l_2+1\right)$;
$k=1$:$f_{i,j,k}=\left(f_{i,j+l1,k^1}+f_{i,j+l1+1,k^1}+\cdots+f_{i,j+r1,k^1}\right)/\left(r_1-l_1+1\right)$;
使用前(后)缀和优化(这里使用后缀和比较方便)。
时间复杂度:$\mathcal O(n^2T)$ 。
由于玄学的原因,我超时了。。。但是用玄学优化后我还是卡进了。。有图为证:
不开玄学优化:
开了玄学优化:
调试记录
- DP 中的一个 $\leq$ 打成了 $<$ 。
代码
#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3) #include <bits/stdc++.h> using namespace std; #define fin cin #define fout cout typedef long double ld; int n, yl, yr, m, sl, sr; ld f[2][505][505]; ld sum[2][505][505]; int main() { ios :: sync_with_stdio(false); int T; fin >> T; while (T--) { fin >> n >> yl >> yr >> m >> sl >> sr; int ly = yr - yl + 1, ls = sr - sl + 1; memset(f, 0, sizeof(f)); memset(sum, 0, sizeof(sum)); for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) { if (j <= yr) f[0][i][j] += min(j-yl, 0) - min(j-yr, 0) + 1; (f[0][i][j] += (sum[1][i][max(j-yl, 0)] - sum[1][i][max(j-yr-1, 0)])) /= ly; sum[0][i][j] = sum[0][i][j-1] + f[0][i][j]; f[1][i][j] = (sum[0][max(i-sl, 0)][j] - sum[0][max(i-sr-1, 0)][j]) / ls; sum[1][i][j] = sum[1][i-1][j] + f[1][i][j]; } fout << fixed << setprecision(3) << f[0][n][m] << endl; } return 0; }