Codeforces

Luogu

分析

有几个比较显然的结论(证明不会)

  • 一定存在一组最优解,使得所有卡都被摆上去过。感性理解:因为 $b_i\geq 0$ 所以你就算摆上去再拿下来也不会更差。
  • 一定存在一组最优解,满足首先摆上 $k-1$ 张卡,再将 $n-k$ 张卡摆上去后立即拿下来,再将剩下的那张卡摆上去。感性理解:此时 $b_i$ 的贡献是最大的,且 $a_i$ 的贡献没有影响。

那么相当于要给所有卡排一个放上去的顺序。考虑二分图匹配,将卡作为左边的点、顺序作为右边的点,连边如下:

  • 对于 $j\in[1,k-1]$,从 $i$ 向 $j$ 连费用为 $a_i+b_i(j-1)$ 的边。
  • 对于 $j\in[k,n-1]$,从 $i$ 向 $j$ 连费用为 $b_i(k-1)$ 的边。
  • 对于 $j=n$,从 $i$ 向 $j$ 连费用为 $a_i+b_i(k-1)$ 的边。

这样子求最大费用最大流,根据残量网络构造方案即可。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=75+10;
const int V=150+10,E=6000+10;

int n,k,a[N],b[N],ans[N];

struct edge { int v,w,c,nxt; } e[E<<1];
int head[V],ecnt;
void addEdge(int u,int v,int w,int c) {
    e[++ecnt]=(edge){v,w,c,head[u]},head[u]=ecnt;
    e[++ecnt]=(edge){u,0,-c,head[v]},head[v]=ecnt;
}

int S,T;
int dis[V],inq[V],lst[V];
int spfa() {
    memset(dis,0x3f,sizeof(dis)); memset(lst,0,sizeof(lst));
    queue<int> Q; Q.push(S); dis[S]=0;
    while (!Q.empty()) {
        int u=Q.front(); Q.pop(),inq[u]=0;
        for (int i=head[u];i;i=e[i].nxt) {
            int v=e[i].v,w=e[i].w,c=e[i].c;
            if (w&&dis[u]+c<dis[v]) {
                dis[v]=dis[u]+c,lst[v]=i;
                if (!inq[v]) Q.push(v),inq[v]=1;
            }
        }
    }
    return lst[T]!=0;
}
int mcmf() {
    int res=0;
    while (spfa()) {
        int f=2e9;
        for (int i=lst[T];i;i=lst[e[i^1].v]) f=min(f,e[i].w);
        for (int i=lst[T];i;i=lst[e[i^1].v]) e[i].w-=f,e[i^1].w+=f;
        res+=f*dis[T];
    }
    return res;
}

int main() {
    for (int _=read();_;--_) {
        memset(head,0,sizeof(head)),ecnt=1;
        n=read(),k=read();
        for (int i=1;i<=n;++i) a[i]=read(),b[i]=read();
        S=0,T=n<<1|1;
        for (int i=1;i<=n;++i) addEdge(S,i,1,0);
        for (int i=1;i<=n;++i) addEdge(i+n,T,1,0);
        for (int i=1;i<=n;++i) {
            for (int j=1;j<k;++j) addEdge(i,j+n,1,-(a[i]+b[i]*(j-1)));
            for (int j=k;j<n;++j) addEdge(i,j+n,1,-b[i]*(k-1));
            addEdge(i,n<<1,1,-(a[i]+b[i]*(k-1)));
        }
        mcmf();
        for (int i=1;i<=n;++i)
            for (int j=head[i];j;j=e[j].nxt) {
                if (e[j].v==S||e[j].w) continue;
                ans[e[j].v-n]=i; break;
            }
        printf("%d\n",k+2*(n-k));
        for (int i=1;i<=n;++i) {
            printf("%d ",ans[i]);
            if (k<=i&&i<n) printf("%d ",-ans[i]);
        }
        puts("");
    }
    return 0;
}
最后修改:2020 年 06 月 10 日 09 : 15 PM