题解:CF75D Big Maximum Sum
温馨提示:本人的代码较长,思路较繁,请谨慎阅读。
很明显的线段树维护最大子段和,先处理出每一个小数组的数据,在放到线段树上面合并即可,合并方式参见 GSS1。
但是这题有一点比较坑,这题中选出来的最大子段不能为空,于是我们需要另外维护一些数据,并且保证选出来至少一个数。然后在求全局最大子段和时就不用向
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pii pair<int,int>
#define endl '\n'
#define pb push_back
#define ls(p) ((p)<<1)
#define rs(p) ((p)<<1|1)
#define lowbit(x) ((x)&(-(x)))
#define abs(x) ((x)>0?(x):(-(x)))
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define gc getchar
#define pc putchar
#define sz(v) ((int)(v.size()))
using namespace std;
mt19937_64 Rand(chrono::steady_clock::now().time_since_epoch().count());
inline int read(){
int x=0,f=1;
char ch=gc();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=gc();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=gc();}
return x*f;
}
inline void print(int x){
if (x<0) pc('-'),x*=-1;
if (x<10) pc(x+'0');
else print(x/10),pc(x%10+'0');
}
const int N=55;
const int M=250005;
struct node{
int len;//小数组的长度
int lmax,rmax,sum;//小数组前缀和的最大值,后缀和的最大值,以及整个数组的和
int ma;//最大子段和
int lmax2,rmax2,ma2;//保证不为空的数据,意义与上面相同
}a[N],t[M<<2];//上面都是 GSS1 的常见操作
int b[M];
void push_up(int p){
t[p].sum=t[ls(p)].sum+t[rs(p)].sum;
t[p].lmax=max(t[ls(p)].lmax,t[ls(p)].sum+t[rs(p)].lmax);
t[p].rmax=max(t[rs(p)].rmax,t[rs(p)].sum+t[ls(p)].rmax);
t[p].ma=max(max(t[ls(p)].ma,t[rs(p)].ma),t[ls(p)].rmax+t[rs(p)].lmax);
t[p].lmax2=max(t[ls(p)].lmax2,t[ls(p)].sum+t[rs(p)].lmax);
t[p].rmax2=max(t[rs(p)].rmax2,t[rs(p)].sum+t[ls(p)].rmax);
t[p].ma2=max(max(t[ls(p)].ma2,t[rs(p)].ma2),max(t[ls(p)].rmax2+t[rs(p)].lmax2,max(t[ls(p)].rmax2+t[rs(p)].lmax,t[ls(p)].rmax+t[rs(p)].lmax2)));//这里要注意细节,可能有很多种情况。
if (p!=1){//不在求全局的时候向 0 取 max
t[p].lmax=max(t[p].lmax,0ll);
t[p].rmax=max(t[p].rmax,0ll);
t[p].ma=max(t[p].ma,0ll);
}
}
void build(int p,int pl,int pr){
if (pl==pr){
t[p]=a[b[pl]];
return ;
}
int mid=(pl+pr)>>1;
build(ls(p),pl,mid);
build(rs(p),mid+1,pr);
push_up(p);
}
int dp[N];
void sol(){
int n,m;
cin>>n>>m;
for (int i=1;i<=n;i++){
cin>>a[i].len;
vector<int> v;v.pb(0);
a[i].lmax=a[i].rmax=a[i].sum=a[i].ma=0;
a[i].lmax2=a[i].rmax2=a[i].ma2=-1e9;//因为不能为空,所以一开始不能取 0
for (int j=1;j<=a[i].len;j++){
int x;
cin>>x;
v.pb(x);
a[i].sum+=x;
}
int now=0;
for (int j=1;j<=a[i].len;j++){
now+=v[j];
a[i].lmax=max(a[i].lmax,now);
a[i].lmax2=max(a[i].lmax2,now);
}
now=0;
for (int j=a[i].len;j>=1;j--){
now+=v[j];
a[i].rmax=max(a[i].rmax,now);
a[i].rmax2=max(a[i].rmax2,now);
}
now=0;
for (int j=1;j<=a[i].len;j++){
now=max(now+v[j],0ll);
a[i].ma=max(a[i].ma,now);
}
int mx=-1e9;
for (int j=1;j<=a[i].len;j++){
mx=max(mx,v[j]);
}
if (mx<0) a[i].ma2=mx;//如果整个数组中所有数都 <0,那么这个数组最大的非空的子段和为数组中的最大值
else a[i].ma2=a[i].ma;//否则,就是最大子段和
}
for (int i=1;i<=m;i++) cin>>b[i];
// for (int i=1;i<=n;i++) cout<<"!! "<<a[i].lmax<<' '<<a[i].rmax<<' '<<a[i].sum<<' '<<a[i].ma<<endl<<"?? "<<a[i].lmax2<<" "<<a[i].rmax2<<" "<<a[i].ma2<<endl;
build(1,1,m);//建线段树
cout<<t[1].ma2;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
while (t--) sol();
return 0;
}