AT_abc400_g
太牛。不会。最好能在这道题上学到点东西。
首先注意到 \(x,y,z\) 这三类中每一类都产生了偶数次贡献,且每一个 \((x,y,z)\) 最多产生一次贡献。
实际上这样选就够了,我们给选到同一类的两两配对,那么实际价值不会比这样算出来的价值小,而且是能取到的。
于是有了一个 DP,设 \(f_{i,j,S}\) 表示前 \(i\) 个 \((x,y,z)\) 选了 \(j\) 个,三类取的个数奇偶性为 \(S\) 时的最大价值。
转移显然,答案即为 \(f_{n,2k,0}\)。
时间复杂度 \(O(nk)\),无法通过。
注意到奇偶性的限制比较松,稍微调一下就能合法。
不考虑奇偶性的限制时直接按 \(\max(x,y,z)\) 排序后取前 \(2k\) 大即可。
那么考虑使用类似做法。先排个序,然后分成前面 \(2k\) 个和后面。
假设前面有至少 \(3\) 个没选,那么后面会选至少 \(3\) 个。
-
不可能同一类在前面没选后面选了,否则可以直接换掉。
-
不可能前面同一类有两个没选后面同一类有两个选了,否则可以直接换掉。
-
不可能前面和后面都是三类,否则可以直接换掉。
于是可以得出结论:前面至多两个不选,后面至多选两个。
于是 DP 中 \(j\) 那一维就可以优化掉了,时间复杂度 \(O(n\log n)\),瓶颈在于排序。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
const ll llinf=3e18;
const int N=1e5+5;
void chk(ll &x,ll y){
x=(x>y)?(x):(y);
}
struct node{
ll w[3];
}a[N];
bool cmp(node a,node b){
return max({a.w[0],a.w[1],a.w[2]})>max({b.w[0],b.w[1],b.w[2]});
}
int n,m;
ll f1[N][3][8],f2[N][3][8];
void slv(){
scanf("%d %d",&n,&m);
rep(i,1,n)rep(j,0,2)scanf("%lld",&a[i].w[j]);
sort(a+1,a+1+n,cmp);
rep(i,0,n+1)rep(j,0,2)rep(k,0,7)f1[i][j][k]=f2[i][j][k]=-llinf;
chk(f1[0][0][0],0);
rep(i,0,m*2-1)rep(j,0,2)rep(k,0,7){
rep(p,0,2)chk(f1[i+1][j][k^(1<<p)],f1[i][j][k]+a[i+1].w[p]);
if(j<2)chk(f1[i+1][j+1][k],f1[i][j][k]);
}
chk(f2[n+1][0][0],0);
per(i,n+1,m*2+2)rep(j,0,2)rep(k,0,7){
chk(f2[i-1][j][k],f2[i][j][k]);
if(j<2)rep(p,0,2)chk(f2[i-1][j+1][k^(1<<p)],f2[i][j][k]+a[i-1].w[p]);
}
ll ans=0;
rep(x,0,2)rep(s,0,7)chk(ans,f1[m*2][x][s]+f2[m*2+1][x][s]);
printf("%lld\n",ans);
}
void main(){
int T=1;
scanf("%d",&T);
while(T--)slv();
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}

浙公网安备 33010602011771号