我看大家都用的dp,也不知道我这个代码是数据太水才过的还是也算是一个正解,因此特发此题解来挨批。
首先我们可以看到,双序***保了同一个数一定有两个数,那么我们可以将这两个数的下标作为一个线段的左右端点,而这两个下标之间的元素之和便是这条线段的权值,那么我们就可以将原问题转换为求 条线段,每条线段都有个权值,从中选择任意的两两不相交的线段,使得所选取的线段权值之和最大。
因此这道题我用的是返回贪心来做的,以右端点进行排序,如果 (左端点)所交的线段权值之和大于
(线段的权值),那么就将
加入进去,如下面代码:
for(int i=1;i<=n;i++){
int cn=query(q[i].r)-query(q[i].l);
if(cn<q[i].x){
add(q[i].r,q[i].x-cn);
ans+=q[i].x-cn;
}
}
前缀和用树状数组维护就行了。 完整代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<unordered_map>
#include<queue>
using namespace std;
#define int long long
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MAXN 1000010
#define debug cout<<"&"<<endl
int b[MAXN];
int a[MAXN];
int tr[MAXN];
int n;
int p[MAXN];
inline int lowbit(int x){
return x&-x;
}
void add(int x,int v){
for(int i=x;i<=2*n;i+=lowbit(i))tr[i]+=v;
}
int query(int x){
int ans=0;
for(int i=x;i;i-=lowbit(i))ans+=tr[i];
return ans;
}
struct node{
int l,r,x;
}q[MAXN];
bool cmp(node a,node b){
return a.r<b.r;
}
void solve(){
// int n;
cin>>n;
for(int i=1;i<=2*n;i++){
cin>>a[i];
}
unordered_map<int,int>mp;
for(int i=1;i<=2*n;i++)b[i]=b[i-1]+a[i];
int cnt=0;
for(int i=1;i<=2*n;i++){
if(mp[a[i]])q[++cnt]={mp[a[i]],i,b[i]-b[mp[a[i]]-1]};
else mp[a[i]]=i;
}
sort(q+1,q+1+n,cmp);
mp.clear();
for(int i=1;i<=n;i++){
mp[q[i].r]=q[i].x;
}
int ans=0;
int tt=0;
for(int i=1;i<=n;i++){
int cn=query(q[i].r)-query(q[i].l);
if(cn<q[i].x){
add(q[i].r,q[i].x-cn);
ans+=q[i].x-cn;
}
}
cout<<ans;
}
signed main(){
ios;
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}
后记:可能是最近贪心题刷的有点多了,看见啥都想贪心一下,然后就想到了反悔贪心,考时没想到完整的证明,可能是非正解,希望大佬点评一下,轻点骂呗qwq。