Gym 101889F — Fundraising 树状数组+离散化+带权上升子序列
题意:给你一些人,每个人有两个特性和一个价值,如果两个人的特性完全相等则可以合并,否则必须一个人的两个特性必须严格大于另一个人才能合并,问:最终可以的到的最大价值。
解题思路:看到别人的博客里面有带权上升子序列,让我想起了最长上升子序列 nlogn 的解法:在数组下标 1—i中最长上升子序列结尾数字最小值(一种贪心)。网址链接:https://blog.csdn.net/qq_37555704/article/details/81479623#2onlogn贪心二分优化。
和这道题有一点相似,这道题用树状数组 root 维护的是 以 R 结尾 可以得到最大价值和。
这道题还有一个条件,必须两个特质必须严格大于,我们可以将左端点升序排列,右端点降序(奇妙之处)。
奇妙之处:如果我们按照右端点升序,当我们更新时,就会影响后面的值,就会造成答案错误,如果你感觉疑惑,你可以自己模拟一下。
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int mod=10000019;
const int INF=0x3f3f3f3f;
const int maxn=1e5+5;
ll root[100005],Right[100005];
struct node
{
int l;
int r;
ll vlaue;
}peo[100005];
bool cmp(node a,node b)
{
return a.l==b.l?a.r>b.r:a.l<b.l;
}
///维护的是 以 R 结尾 可以得到最大价值和
void add_root(int i,ll x)
{
while(i<maxn)
{
root[i]=max(x,root[i]);
i=i+(i&(-i));
}
return ;
}
ll find_root(int i)
{
ll cnt=0;
while(i)
{
cnt=max(cnt,root[i]);
i=i-(i&(-i));
}
return cnt;
}
int main()
{
memset(root,0,sizeof(root));
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d %lld",&peo[i].l,&peo[i].r,&peo[i].vlaue);
Right[i]=peo[i].r; ///记录右端点
}
sort(peo+1,peo+1+n,cmp);
sort(Right+1,Right+1+n); ///离散化
int len=unique(Right+1,Right+1+n)-Right-1;
for(int i=1;i<=n;i++)
{ ///合并相同特性的人
if(i<n&&peo[i].l==peo[i+1].l&&peo[i].r==peo[i+1].r)
{
peo[i+1].vlaue+=peo[i].vlaue;
continue;
}
///pos+1为peo[i]第几大
int pos=lower_bound(Right+1,Right+1+len,peo[i].r)-Right-1;
ll cnt=find_root(pos); ///查询最大值
add_root(pos+1,cnt+peo[i].vlaue); ///更新最大值
///因为当L相等时,R为降序排列,所以更新时不会影。
}
printf("%lld\n",find_root(len));
return 0;
}