开学第九测
开学第九测
一不小心从第六蹦到了第九,第七还没写,第八还没写完 还是太懒了啊 2333
这套题是 dalao RMY 自己出从网上扒的 而且他自己还不会
T1 模拟只会猜题意
思路:数据输入同时处理处前缀和,然后枚举区间长度,处理处 1 ~ n 的答案,随后查询即可
时间复杂度:O( n2 + m )
#include<iostream> #include<cstdio> #define M 100005 using namespace std; int n, m; int a[M], sum[M], ans[M]; int read() { int x = 0, c, f = 1; while(!isdigit(c = getchar())) if(c == '-') f = -1; while(x = x * 10 + c - '0', isdigit(c = getchar())); return x * f; } int main(){ // freopen("test.in","r",stdin); // freopen("test.out","w",stdout); n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(), sum[i] = sum[i - 1] + a[i]; for(int i = 1; i <= n; i++) { ans[i] = -1e9; for(int j = 1; i + j <= n + 1; j++) ans[i] = max(ans[i], sum[j + i - 1] - sum[j - 1]); } for(int i = n - 1; i >= 1; i--) ans[i] = max(ans[i], ans[i+1]); while(m--) printf("%d\n", ans[read()]); return 0; }
T2 DP一般看规律
思路:分四种情况讨论
dp[i][j] 为找到 i 种 bug j 个子系统 到达目标的期望
dp[i][j] 找到一个新 bug 属于已找到的种类并且属于已找到的子系统概率 p1 = i/n * j/s;
dp[i+1][j] 找到一个新 bug 不属于已找到的种类属于已找到的子系统概率 p2 = (n-i)/n * j/s;
dp[i][j+1] 找到一个新bug不属于已找到的系统属于已找到的种类 概率 p3 = i/n * (n-j)/s;
dp[i+1][j+1] 找到一个新 bug 既不属于已找到的系统也不属于已找到的种类 概率 p4 = (n-i)/n * (s-j)/s;
所以dp方程为 dp[i][j] = p1*dp[i][j] + p2*dp[i+1][j] + p3*dp[i][j+1] + p4*dp[i+1][j+1] + 1;
#include<cstdlib> #include<cstring> #include<cstdio> #define M 1005 using namespace std; int n, s; double p1, p2, p3, p4; double dp[M][M]; int main() { // freopen("bug.in","r",stdin); // freopen("bug.out","w",stdout); while(scanf("%d%d", &n, &s) != EOF) { memset(dp, 0, sizeof dp); for(int i = n; i >= 0; i--) { for(int j = s; j >= 0; j--) { if(i == n && j == s) continue; p1 = 1.0 * i/n * j/s; p2 = 1.0 * (n-i)/n * j/s; p3 = 1.0 * i/n * (s-j)/s; p4 = 1.0 * (n-i)/n * (s-j)/s; dp[i][j] = (p2*dp[i+1][j] + p3*dp[i][j+1] + p4*dp[i+1][j+1] + 1.0) / (1.0 - p1); } } printf("%.4f\n", dp[0][0]); } // fclose(stdin);fclose(stdout); return 0; }
T3 数学上来先打表
思路:我们先想暴力怎么做: 把一段区间的数取出来, 排个序, 从小到大选择。如果 a1~ai−1 能够表示 1 x,此时加入 ai,如果 ai≤x+1,那么就可以表示 x+ai,否则 x 就是答案。
试着优化一下这个过程: 设ai−1=k, ai=y, 1~i-1的神秘数为ans=x+1,那么显然ans=∑t=1i−1。 此时如果存在k+1~ans的数就可以更新ans。更具体地,如果 k+1~ans 内的数的和为 s,那么 ans+=s;而 ans 为1~k 的数的和+1,故 ans 的新值应该赋为 1~ans 的数的和。
说了这么多废话有什么用?我们可以发现每次 ans 的增量都大于等于前一次的 ans,所以这个过程的时间复杂度应该为 O(loga)。
而事实上我们并不能把区间拿出来排序,所以需要使用数据结构,上一个主席树就好了。 然而并不会写 2333
时间复杂度为 O(nlog2n)
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<string> #include<cstdio> #include<cmath> #define M 200005 #define MM 5000005 using namespace std; int n, m, mm, ind; int s[MM], a[M]; int root[MM], ch[MM][2]; inline void update(int &root, int oldrt, int l, int r, int x) { root = ++ind; if(l == r) { s[root] = s[oldrt] + x; return ; } int mid = l+r >> 1; //这里是先算 l+r ,后算 >> 1 if(x <= mid) ch[root][1] = ch[oldrt][1], update(ch[root][0], ch[oldrt][0], l, mid, x); if(x > mid) ch[root][0] = ch[oldrt][0], update(ch[root][1], ch[oldrt][1], mid+1, r, x); s[root] = s[ch[root][0]] + s[ch[root][1]]; } inline int query(int rootx, int rooty, int l, int r, int ql, int qr) { if(ql <= l && r <= qr) return s[rootx] - s[rooty]; int mid = l+r >> 1; if(qr <= mid) return query(ch[rootx][0], ch[rooty][0], l, mid, ql, qr); else if(ql > mid) return query(ch[rootx][1], ch[rooty][1], mid+1, r, ql, qr); else return query(ch[rootx][0], ch[rooty][0], l, mid, ql, qr) + query(ch[rootx][1], ch[rooty][1], mid+1, r, ql, qr); } inline int qu(int l, int r) { int lasgg = 1111111111; for (int t = 1, gg; ; ) { gg = query(root[r], root[l-1], 1, mm, 1, t); if(gg == lasgg) return t; t = gg + 1; lasgg = gg; } } int main() { // freopen("mystic.in","r",stdin); // freopen("mystic.out","w",stdout); scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); if(a[i] > mm) mm = a[i]; } for(int i = 1; i <= n; i++) update(root[i], root[i - 1], 1, mm, a[i]); scanf("%d", &m); for(int i = 1, l, r; i <= m; i++) { scanf("%d%d", &l, &r); printf("%d\n", qu(l, r)); } // fclose(stdin); fclose(stdout); return 0; }