最大m子段和问题

普通版

题目大意

       ~~~~~~       一个长度为 n n n 的序列 a 1 . . . a n a_1...a_n a1...an,让你从中选出 m m m 个连续段,使得选出来的和最大。

O ( n m ) O(nm) O(nm)

       ~~~~~~        f i , j f_{i,j} fi,j 表示前 i i i 个数,共选了 j j j 个连续段,所得到的最大和。
       ~~~~~~        g i , j = max ⁡ { f 1.. i , j } g_{i,j}=\max\{ f_{1..i, j} \} gi,j=max{f1..i,j}, 设 S S S 表示 a a a 的前缀和。
       f i , j = max ⁡ { S i − S k + g k , j − 1 } ~~~~~~f_{i,j}=\max\{ S_i-S_k+g_{k,j-1} \}       fi,j=max{SiSk+gk,j1}
       ~~~~~~       然后搞一下就是 O ( n m ) O(nm) O(nm) 的了。

O ( m   l o g   n ) O(m~log~n) O(m log n)

       ~~~~~~       源点向每一个点 i i i 连一条容量为 1 1 1、费用为 a i a_i ai 的边,每个点 i i i i + 1 i+1 i+1 连一条容量为 1 1 1、费用为 a i + 1 a_{i+1} ai+1 的边,每个点向汇点连一条容量为 1 1 1、费用为 0 0 0 的边。
       ~~~~~~       这样建图,每增广一次,相当于取出了一个连续段,又由于有反向弧这种操作,所以直接增广 m m m 次就可以了。(当然,如果增广出来是负数,可以直接退出)

       ~~~~~~       当然直接跑费用流肯定会 T。
       ~~~~~~       引入费用流只是为了引入反向弧这种机制。我们发现实际上是每次贪心地选取一个最大子段而已,那么选完之后我们就把这段取反就行了。
       ~~~~~~       这个用线段树实现,维护的变量挺多的。

加强版

题目大意

       ~~~~~~       给你一个长度为 n n n 的序列 a 1 . . . a n a_1...a_n a1...an,有如下两种操作:
       1   x   y ~~~~~~1~x~y       1 x y:将 a x a_x ax 的值改为 y y y
       2   l   r   k ~~~~~~2~l~r~k       2 l r k:询问区间 [ l , r ] [l, r] [l,r] 的最大 k k k 子段和。

O ( m   l o g   n ) O(m~log~n) O(m log n)

       ~~~~~~       像上面那样,既然都已经打了个线段树了,那就再加个单点修改。

代码

普通

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=5e4+5;

struct TR{
	LL s,sl,sr,st;
	int wzi,wzj,wzl,wzr;
};

int n,m;
LL a[maxn];

TR tr[2][4*maxn];
bool bz[4*maxn];
TR merge(TR a,TR b)
{
	TR re;
	re.s=a.s+b.s;
	
	if (a.st>b.st)
	{
		re.st=a.st;
		re.wzi=a.wzi, re.wzj=a.wzj;
	} else
	{
		re.st=b.st;
		re.wzi=b.wzi, re.wzj=b.wzj;
	}
	if (a.sr+b.sl>re.st)
	{
		re.st=a.sr+b.sl;
		re.wzi=a.wzr, re.wzj=b.wzl;
	}
	
	if (a.sl>a.s+b.sl)
	{
		re.sl=a.sl;
		re.wzl=a.wzl;
	} else
	{
		re.sl=a.s+b.sl;
		re.wzl=b.wzl;
	}
	
	if (b.sr>b.s+a.sr)
	{
		re.sr=b.sr;
		re.wzr=b.wzr;
	} else
	{
		re.sr=b.s+a.sr;
		re.wzr=a.wzr;
	}
	
	return re;
}
void tr_js(int k,int l,int r)
{
	if (l==r)
	{
		tr[0][k].s=tr[0][k].sl=tr[0][k].sr=tr[0][k].st=a[l];
		tr[0][k].wzi=tr[0][k].wzj=tr[0][k].wzl=tr[0][k].wzr=l;
		tr[1][k].s=tr[1][k].sl=tr[1][k].sr=tr[1][k].st=-a[l];
		tr[1][k].wzi=tr[1][k].wzj=tr[1][k].wzl=tr[1][k].wzr=l;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	tr_js(t,l,t1), tr_js(t+1,t1+1,r);
	tr[0][k]=merge(tr[0][t],tr[0][t+1]);
	tr[1][k]=merge(tr[1][t],tr[1][t+1]);
}
void update(int k,int t)
{
	if (!bz[k]) return;
	swap(tr[0][t],tr[1][t]);
	swap(tr[0][t+1],tr[1][t+1]);
	bz[t]^=bz[k];
	bz[t+1]^=bz[k];
	bz[k]=0;
}
void xg_many(int k,int l,int r,int x,int y)
{
	if (l==x && r==y)
	{
		swap(tr[0][k],tr[1][k]);
		bz[k]^=1;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t);
	if (y<=t1) xg_many(t,l,t1,x,y);
		else if (x>t1) xg_many(t+1,t1+1,r,x,y);
			else xg_many(t,l,t1,x,t1), xg_many(t+1,t1+1,r,t1+1,y);
	tr[0][k]=merge(tr[0][t],tr[0][t+1]);
	tr[1][k]=merge(tr[1][t],tr[1][t+1]);
}

LL get(int m)
{
	LL re=0;
	while (m--)
	{
		if (tr[0][1].st<0) break;
		re+=tr[0][1].st;
		xg_many(1,1,n,tr[0][1].wzi,tr[0][1].wzj);
	}
	return re;
}

int main()
{
	scanf("%d %d",&n,&m);
	fo(i,1,n) scanf("%lld",&a[i]);
	tr_js(1,1,n);
	
	printf("%lld\n",get(m));
}

加强

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int maxn=1e5+5;

struct TR{
	int s,sl,sr,st,wzi,wzj,wzl,wzr;
};

int n,a[maxn];

TR tr[2][4*maxn];
bool bz[4*maxn];
TR merge(TR a,TR b)
{
	TR re;
	re.s=a.s+b.s;
	
	if (a.st>b.st)
	{
		re.st=a.st;
		re.wzi=a.wzi, re.wzj=a.wzj;
	} else
	{
		re.st=b.st;
		re.wzi=b.wzi, re.wzj=b.wzj;
	}
	if (a.sr+b.sl>re.st)
	{
		re.st=a.sr+b.sl;
		re.wzi=a.wzr, re.wzj=b.wzl;
	}
	
	if (a.sl>a.s+b.sl)
	{
		re.sl=a.sl;
		re.wzl=a.wzl;
	} else
	{
		re.sl=a.s+b.sl;
		re.wzl=b.wzl;
	}
	
	if (b.sr>b.s+a.sr)
	{
		re.sr=b.sr;
		re.wzr=b.wzr;
	} else
	{
		re.sr=b.s+a.sr;
		re.wzr=a.wzr;
	}
	
	return re;
}
void tr_js(int k,int l,int r)
{
	if (l==r)
	{
		tr[0][k].s=tr[0][k].sl=tr[0][k].sr=tr[0][k].st=a[l];
		tr[0][k].wzi=tr[0][k].wzj=tr[0][k].wzl=tr[0][k].wzr=l;
		tr[1][k].s=tr[1][k].sl=tr[1][k].sr=tr[1][k].st=-a[l];
		tr[1][k].wzi=tr[1][k].wzj=tr[1][k].wzl=tr[1][k].wzr=l;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	tr_js(t,l,t1), tr_js(t+1,t1+1,r);
	tr[0][k]=merge(tr[0][t],tr[0][t+1]);
	tr[1][k]=merge(tr[1][t],tr[1][t+1]);
}
void update(int k,int t)
{
	if (!bz[k]) return;
	swap(tr[0][t],tr[1][t]);
	swap(tr[0][t+1],tr[1][t+1]);
	bz[t]^=bz[k];
	bz[t+1]^=bz[k];
	bz[k]=0;
}
void xg_single(int k,int l,int r,int x,int z)
{
	if (l==r)
	{
		tr[0][k].s=tr[0][k].sl=tr[0][k].sr=tr[0][k].st=z;
		tr[1][k].s=tr[1][k].sl=tr[1][k].sr=tr[1][k].st=-z;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t);
	if (x<=t1) xg_single(t,l,t1,x,z); else xg_single(t+1,t1+1,r,x,z);
	tr[0][k]=merge(tr[0][t],tr[0][t+1]);
	tr[1][k]=merge(tr[1][t],tr[1][t+1]);
}
void xg_many(int k,int l,int r,int x,int y)
{
	if (l==x && r==y)
	{
		swap(tr[0][k],tr[1][k]);
		bz[k]^=1;
		return;
	}
	int t=k<<1, t1=(l+r)>>1;
	update(k,t);
	if (y<=t1) xg_many(t,l,t1,x,y);
		else if (x>t1) xg_many(t+1,t1+1,r,x,y);
			else xg_many(t,l,t1,x,t1), xg_many(t+1,t1+1,r,t1+1,y);
	tr[0][k]=merge(tr[0][t],tr[0][t+1]);
	tr[1][k]=merge(tr[1][t],tr[1][t+1]);
}
TR tr_cx(int k,int l,int r,int x,int y)
{
	if (l==x && r==y) return tr[0][k];
	int t=k<<1, t1=(l+r)>>1;
	update(k,t);
	if (y<=t1) return tr_cx(t,l,t1,x,y);
		else if (x>t1) return tr_cx(t+1,t1+1,r,x,y);
			else return merge(tr_cx(t,l,t1,x,t1), tr_cx(t+1,t1+1,r,t1+1,y));
}

int z[maxn][2],z0;
int get(int m,int l,int r)
{
	int re=0;
	while (m--)
	{
		TR t=tr_cx(1,1,n,l,r);
		if (t.st<=0) break;
		re+=t.st;
		z[++z0][0]=t.wzi, z[z0][1]=t.wzj;
		xg_many(1,1,n,t.wzi,t.wzj);
	}
	for(; z0; z0--) xg_many(1,1,n,z[z0][0],z[z0][1]);
	return re;
}

int m;
int main()
{
	scanf("%d",&n);
	fo(i,1,n) scanf("%d",&a[i]);
	tr_js(1,1,n);
	
	scanf("%d",&m);
	while (m--)
	{
		int ty,l,r,k;
		scanf("%d %d %d",&ty,&l,&r);
		if (ty==0)
		{
			xg_single(1,1,n,l,r);
		} else
		{
			scanf("%d",&k);
			printf("%d\n",get(k,l,r));
		}
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值