【20191102】考试

T1 最大K段和

在这里插入图片描述在这里插入图片描述

考场上想了一个贪心:处理出所有仅含正数的极大区间。如果区间数量 ≤ m \leq m m,直接把所有正数的和输出即可。否则,我们选取前 m m m大区间,进行以下操作使结果最大化:
· 合并两个已选区间,尝试选取更多未选区间
· 合并一个已选区间和一个未选区间
实现比较繁琐,敬请参考代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mn = 100005;
struct node{
    int l, r, id; ll sum;
    bool operator< (const node x) {return sum > x.sum;}
}h[mn], tmp[mn];
int a[mn], cnt, cho[mn], tot;
ll s[mn];
bool vis[mn];
int main()
{
    int n, m; ll ans = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), s[i] = s[i - 1] + 1ll * a[i];
    for(int i = 1; i <= n; i++)
        if(a[i] >= 0)
        {
            h[++cnt] = (node) {i, i + 1, 0, a[i]}, h[cnt].id = cnt;
            while(h[cnt].r <= n && a[h[cnt].r] >= 0) h[cnt].sum += a[h[cnt].r], ++h[cnt].r;
            --h[cnt].r, i = h[cnt].r;
        }
    if(cnt <= m)
    {
        for(int i = 1; i <= cnt; i++) ans += h[i].sum;
        printf("%lld\n", ans); return 0;
    }
    for(int i = 1; i <= cnt; i++) tmp[i] = h[i];
    sort(tmp + 1, tmp + 1 + cnt);
    for(int i = 1; i <= m; i++) ans += tmp[i].sum, vis[tmp[i].id] = 1, cho[++tot] = tmp[i].id;
    ++m; int las = 1;
    for(int i = 1; i <= cnt && m <= cnt; )
        if(vis[i])
        {
            bool flg = 0;
            for(int j = las + 1; j <= cnt; j++)
                if(vis[j])
                {
                    flg = 1;
                    if(h[i].r < tmp[m].l && tmp[m].r < h[j].l)
                    {
                        ll del = max(s[h[j].l - 1] - s[tmp[m].r], s[tmp[m].l-1] - s[h[i].r]);
                        if(del + tmp[m].sum >= 0)
                            ans += del + tmp[m].sum, vis[tmp[m].id] = 1, cho[++tot] = tmp[m].id, i = (del == (s[tmp[m].l-1] - s[h[i].r]) ? tmp[m].id : j), ++m;
                    }
                    else if(tmp[m].sum + s[h[j].l-1] - s[h[i].r] >= 0)
                        ans += tmp[m].sum + s[h[j].l-1] - s[h[i].r], vis[tmp[m].id] = 1, cho[++tot] = tmp[m].id, i = j, ++m;
                    else i = j;
                    if(i == j) las = j;
                    else las = j - 1;
                    break;
                }
            if(!flg)
                break;
        }
        else ++i;
    sort(cho + 1, cho + 1 + tot);
    for(int i = 1; i <= cnt; i++)
        if(!vis[i])
        {
            int r = lower_bound(cho + 1, cho + tot + 1, i) - cho;
            if(r == tot)
            {
                ll del = s[h[i].l-1] - s[h[cho[r]].r];
                if(del + h[i].sum >= 0) ans += del + h[i].sum, cho[r] = i;
            }
            int l = cho[r - 1], pos = r; r = cho[r]; ll del = max(s[h[r].l - 1] - s[h[i].r], s[h[i].l-1] - s[h[l].r]);
            if(del + h[i].sum >= 0)
            {
                ans += del + h[i].sum;
                if(del == s[h[i].l-1] - s[h[l].r]) cho[pos] = i;
                else i = pos;
            }
        }
    printf("%lld\n", ans);
}


T2

就是对原序列镜像以后求LIS

#include<bits/stdc++.h>
using namespace std;
const int mn = 100005;
int a[mn << 1], f[mn << 1];
int c[mn], num[mn], cnt;
inline void update(int p, int v) {while(p <= cnt) c[p] = max(c[p], v), p += p & -p;}
inline int getmax(int p)
{
    int ret = 0;
    while(p) ret = max(ret, c[p]), p -= p & -p;
    return ret;
}
int main()
{
    //freopen("dequexlis.in", "r", stdin);
    //freopen("dequexlis.out","w",stdout);
    int n;
    scanf("%d", &n);
    for(int i = n + 1; i <= (n << 1); i++)
        scanf("%d", &a[i]), a[(n << 1) - i + 1] = a[i], num[++cnt] = a[i];
    n <<= 1, sort(num + 1, num + 1 + cnt), cnt = unique(num + 1, num + 1 + cnt) - num - 1;
    for(int i = 1; i <= n; i++)
        a[i] = lower_bound(num + 1, num + 1 + cnt, a[i]) - num;
    for(int i = 1; i <= n; i++)
        f[i] = getmax(a[i] - 1) + 1, update(a[i], f[i]);
    printf("%d\n", *max_element(f + 1, f + 1 + n));
}

T3 最大前缀和在这里插入图片描述

在这里插入图片描述
机房dalao想出了 O ( n ) O(n) O(n)方法。
我们把 + 1 +1 +1 − 1 -1 1看成在坐标系中走路,+1为往右上方走,-1往右下方走(如图)
在这里插入图片描述
那么原问题就相当于从图中的(0,0)走到(n+m,n-m)。由题意不难得出,走过去的方案数为 C n + m n \Large C_{n+m}^n Cn+mn
原问题要求我们求所有前缀最大值之和。不妨枚举这个最大值 i i i(显然
i ∈ [ n − m , n ] i \in [n-m, n] i[nm,n]),然后考虑计算每一个最大值的贡献。
枚举了 i i i后,就是要求恰好过 y = i y=i y=i的折线数目。不妨先求不超过 y = i y=i y=i的折线数,那么就可以用总方案数-不合法数计算,即 C n + m n − C n + m m + i + 1 \Large C_{n+m}^n-C_{n+m}^{m+i+1} Cn+mnCn+mm+i+1。之后再减去在 y = i − 1 y=i-1 y=i1及以下的数目即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mn = 8005, mod = 998244853;
ll frac[mn], inv[mn];
inline void init(int n)
{
    frac[0] = 1;
    for(int i = 1; i <= n; i++)
        (frac[i] = frac[i-1] * 1ll * i) %= mod;
    inv[0] = inv[1] = 1;
    for(int i = 2; i <= n; i++)
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    for(int i = 2; i <= n; i++)
        (inv[i] *= inv[i-1]) %= mod;
}
inline ll C(int n, int m) {return n < m ? 0 : frac[n] * inv[m] % mod * inv[n - m] % mod;}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m), init(n + m);
    ll ans = 0, las = 0;
    for(int i = n - m; i <= n; i++)
    {
        ll tmp = (((C(n + m, n) - C(n + m, m + i + 1)) % mod - las) % mod + mod) % mod;
        (las += tmp) %= mod;
        if(i > 0) (ans += tmp * 1ll * i) %= mod;
    }
    printf("%lld\n", ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值