2020.1.15考试总结

T1
01分数规划
很明显我们需要的区间最大值和最小值在区间两端,因为有L的限制,所以我们可以先做一遍长度为L的滑动窗口。
问题判定的转化,设我们二分的值是v:
\(\displaystyle \frac{A[r]-A[l]}{r-l+k} > v\)
\(\displaystyle (A[r]-rv)-(A[l]-lv) > kv\)
再用一个单调队列维护一下就好了。

#include <iostream>
#include <cstdio>
#define LL long long
#define DB double
using namespace std;
int T, n, k, L, R, head1 = 1, tail1, head2 = 1, tail2;
DB ans;
const int N = 50010;
const DB eps = 1e-7;
int a[N], q1[N], q2[N]; /*小 大*/
DB b[N];
inline int read() 
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * f;
}
int dd(DB mid) 
{
    head1 = head2 = 1; tail1 = tail2 = 0;
    for (int i = L; i <= n; ++i) 
    {
        while (head1 <= tail1 && b[i - L + 1] <= b[q1[tail1]])--tail1;
        q1[++tail1] = i - L + 1;
        while (head1 <= tail1 && i - q1[head1] + 1 > R)++head1;
        if (b[i] - b[q1[head1]] >= mid * k)return 1;
    }
    return 0;
}
int check(DB mid) 
{
    for (int i = 1; i <= n; ++i)b[i] = a[i] - mid * i;    if (dd(mid))return 1;
    for (int i = 1; i <= n; ++i)b[i] = a[n - i + 1] - mid * i; if (dd(mid))return 1;
    return 0;
}
void solve() 
{
    DB l, r, mid;
    head1 = head2 = 1; tail1 = tail2 = 0;
    for (int i = 1; i <= n; ++i) 
    {
        while (head1 <= tail1 && a[i] <= a[q1[tail1]])--tail1;
        while (head2 <= tail2 && a[i] >= a[q2[tail2]])--tail2;
        q1[++tail1] = i; q2[++tail2] = i;
        while (head1 <= tail1 && i - q1[head1] + 1 > L)++head1;
        while (head2 <= tail2 && i - q2[head2] + 1 > L)++head2;
        ans = max(ans, (DB)(a[q2[head2]] - a[q1[head1]]) / (L - 1 + k));
    }
    l = 0; r = 1000;
    while (r - l > eps) 
    {
        mid = (l + r) / 2;
        if (check(mid))l = mid;
        else r = mid;
    }
    printf("%.4f\n", max(ans, mid));
}
int main() 
{
    cin >> T;
    while (T--) 
    {
        n = read(); k = read(); L = read(); R = read(); ans = 0;
        for (int i = 1; i <= n; ++i)a[i] = read();
        solve();
    }
    fclose(stdin); fclose(stdout);
    return 0;
}

T2 给定一个长度为 \(n\) 的序列 \(a_i\), 求有多少个区间的和 \(\%k\) 等于区间的最小值。(\(n \le 2 \times 10^5\))

看到这类题首先想到每一个值的贡献,确定左右边界就用单调栈。
然后左右边界那边小就枚举哪一边,类似于启发式合并。
时间复杂度\(O(nlog^2n)\)
关于边界的细节处理比较复杂,需要注意。

#include <iostream>
#include <cstdio>
#define int long long
#define LL long long
using namespace std;
int n, k, ans, top, cnt;
const int N = 200010;
int a[N], zhan[N], l[N], r[N], s[N], root[N];
int sum[N * 100], lson[N * 100], rson[N * 100];
inline int read() 
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * f;
}
void change(int pre, int &k, int l, int r, int pos, int val) 
{
    if (!k)k = ++cnt; sum[k] = sum[pre] + val;
    if (l == r)return; int mid = (l + r) >> 1;
    if (pos <= mid)rson[k] = rson[pre], change(lson[pre], lson[k], l, mid, pos, val);
    else lson[k] = lson[pre], change(rson[pre], rson[k], mid + 1, r, pos, val);
}
int ask(int pre, int k, int l, int r, int pos) 
{
    if (l == r)return sum[k] - sum[pre];
    int mid = (l + r) >> 1;
    if (pos <= mid)return ask(lson[pre], lson[k], l, mid, pos);
    else return ask(rson[pre], rson[k], mid + 1, r, pos);
}
signed main() 
{
    cin >> n >> k;
    change(root[0], root[1], 0, k, 0, 1);
    for (int i = 2; i <= n + 1; ++i)a[i] = read(), s[i] = (a[i] + s[i - 1]) % k, change(root[i - 1], root[i], 0, k, s[i], 1);
    for (int i = 2; i <= n + 1; ++i) 
    {
        while (top && a[i] <= a[zhan[top]])r[zhan[top--]] = i;
        l[i] = zhan[top]; zhan[++top] = i;
    }
    for (int i = 2; i <= n + 1; ++i) 
    {
        if (!l[i])l[i] = 1;
        if (!r[i])r[i] = n + 2;
    }
    for (int i = 2; i <= n + 1; ++i) 
    {
        if (a[i] >= k)continue;
        if (i - l[i] < r[i] - i)for (int j = l[i] + 1; j <= i; ++j)ans += ask(root[i - 1], root[r[i] - 1], 0, k, (s[j - 1] + a[i]) % k);
        else for (int j = i; j <= r[i] - 1; ++j)ans += ask(root[l[i] - 1], root[i - 1], 0, k, (s[j] - a[i] + k) % k);
    }
    cout << ans;
    fclose(stdin); fclose(stdout);
    return 0;
}

T3 给定你n,m,求\(\displaystyle \sum_{i=1}^ni^mm^i\) (\(mod\) \(10^9+7\)) (\(n \le 10^9 ,m \le 1000\))

老师说是套路题...
我们设\(\displaystyle f(j)=\sum_{i=1}^ni^jm^i\)
那么
\[ \begin{aligned} \displaystyle (m-1)f(j)=&\sum_{i=1}^n i^j m^{i+1} - \sum_{i=1}^ni^jm^i\\ \displaystyle (m-1)f(j)=&\sum_{i=2}^{n+1} (i-1)^j m^{i} - \sum_{i=1}^{n}i^{j}m^{i}\\ \displaystyle (m-1)f(j)=&\sum_{i=1}^{n} (i-1)^j m^{i} - \sum_{i=1}^{n}i^{j}m^{i}+n^{j}m^{n+1}\\ \displaystyle (m-1)f(j)=&\sum_{i=1}^{n}((i-1)^j - i^{j})m^{i}+n^{j}m^{n+1}\\ \displaystyle (m-1)f(j)=&\sum_{i=1}^{n}(\sum_{k=0}^{j}C_{j}^{k}i^{k}(-1)^{j-k}-i^j)m^{i}+n^{j}m^{n+1}\\ \displaystyle (m-1)f(j)=&\sum_{i=1}^{n}(\sum_{k=0}^{j-1}C_{j}^{k}i^{k}(-1)^{j-k})m^{i}+n^{j}m^{n+1}\\ \displaystyle (m-1)f(j)=&\sum_{k=0}^{j-1}C_{j}^{k}(-1)^{j-k}(\sum_{i=1}^{n}i^{k}m^{i})+n^{j}m^{n+1}\\ \displaystyle (m-1)f(j)=&\sum_{k=0}^{j-1}C_{j}^{k}(-1)^{j-k}f(k)+n^{j}m^{n+1}\\ \displaystyle f(j)=&\frac{\displaystyle \sum_{k=0}^{j-1}C_{j}^{k}(-1)^{j-k}f(k)+n^{j}m^{n+1}}{m-1}\\ \end{aligned} \]
然后就是一个和\(n\)无关的\(O(m^2)\)的递推式了.

注意\(f(0)\)需要单独求。

#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
#define LL long long
using namespace std;
int n, m;
const int mod = 1e9 + 7, N = 1005;
LL ans;
LL jc[N], inv[N], F[1005];
inline int read() 
{
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    return x * f;
}
LL ksm(LL a, LL b, LL mod) 
{
    LL res = 1;
    for (; b; b >>= 1, a = a * a % mod)
        if (b & 1)res = res * a % mod;
    return res;
}
LL C(int n, int m) {return jc[n] * inv[m] % mod * inv[n - m] % mod;}
LL f(int k) 
{
    if (F[k] != -1)return F[k];
    LL res = 0;
    for (int j = 0; j <= k - 1; ++j)(res += ((k - j) & 1 ? -1 : 1) * C(k, j) % mod * f(j)) %= mod;
    (res += ksm(n, k, mod) * ksm(m, n + 1, mod) % mod) %= mod;
    return F[k] = res * ksm(m - 1, mod - 2, mod) % mod;
}
signed main() 
{
    memset(F, -1, sizeof(F));
    cin >> n >> m;
    jc[0] = jc[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= 1000; ++i)jc[i] = jc[i - 1] * i % mod;
    inv[1000] = ksm(jc[1000], mod - 2, mod);
    for (int i = 999; i >= 1; --i)inv[i] = inv[i + 1] * (i + 1) % mod;
    F[0] = (ksm(m, n + 1, mod) - m) * ksm(m - 1, mod - 2, mod) % mod;
    cout << (f(m) % mod + mod) % mod;
    fclose(stdin); fclose(stdout);
    return 0;
}
全部评论

相关推荐

威猛的小饼干正在背八股:挂到根本不想整理
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务