传送门

题意:对于一个长度为n的序列,选取k个子序列(每个点只能被选一次),求k个子序列的总和最大。

对于每个点拆为入点和出点,连接入点到出点费用为负的点权,流量为1,源点SS连接所有点入点,所有点出点连接汇点T,流量为1,费用为0,对于每个点ai,往后面找最多100个权值大于等于ai的 j 点(别问为啥100个,玄学),i 的出点连接 j 的入点,最后超级源S连接SS,流量为k,费用为0,然后跑一遍费用流即可。

#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int maxn = 4020;
const int inf = 0x3f3f3f3f;
struct Edge
{
    int from,to,cap,flow,cost;
    Edge(int a,int b,int c,int d,int e)
    {
        from = a,to = b,cap = c,flow = d,cost = e;
    }
};
struct MCMF
{
    int n, m;
    vector<Edge>edges;
    vector<int>G[maxn];
    int inq[maxn];
    int d[maxn];
    int p[maxn];
    int a[maxn];
    void Init(int n)
    {
        this -> n = n;
        for(int i = 0;i <= n; i++)
            G[i].clear();
        edges.clear();
    }
    void add_edge(int from,int to,int cap,int cost)
    {
        edges.push_back(Edge(from,to,cap,0,cost));
        edges.push_back(Edge(to,from,0,0,-cost));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }
    bool bellman(int s,int t,int& flow,int& cost)
    {
        for(int i = 0;i <= n; i++)
            d[i] = inf,inq[i] = 0;
        d[s] = 0;inq[s] = 1;p[s] = 0;a[s] = inf;
        queue<int>Q;
        Q.push(s);
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            inq[u] = 0;
            for(int i = 0;i < G[u].size(); i++)
            {
                Edge& e = edges[G[u][i]];
                if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
                {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u],e.cap - e.flow);
                    if(!inq[e.to])
                    {
                        Q.push(e.to);
                        inq[e.to] = 1;
                    }
                }
            }
        }
        if(d[t] == inf)
            return false;
        flow += a[t];
        cost += d[t] * a[t];
        int u = t;
        while(u != s)
        {
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }
    int minCost(int s,int t)
    {
        int flow = 0,cost = 0;
        while(bellman(s,t,flow,cost));
        return cost;
    }
}solve;
int a[maxn];
signed main()
{
    //freopen("in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n, &k);
        for(int i = 1;i <= n; i++)
            scanf("%d",&a[i]);
        int s0 = 2 * n + 1,s1 = s0 + 1,t = s1 + 1;
        solve.Init(t);
        solve.add_edge(s0,s1,k,0);
        for(int i = 1;i <= n; i++)
        {
            solve.add_edge(s1,i,1,0);
            solve.add_edge(i,i + n,1,-a[i]);
            solve.add_edge(i + n,t,1,0);
        }
        for(int i = 1;i <= n; i++)
        {
            for (int j = i + 1, ok = 0; j <= n && ok <= 100; j++)
            {
                if (a[j] >= a[i])
                    ok++, solve.add_edge(i + n, j, 1, 0);
            }
        }
        printf("%d\n",-solve.minCost(s0,t));
    }
    return 0;
}