codeforces 1108 E2. Array and Segments (线段树,区间修改)

题意:给你n个数,m个范围,可以选择任意个范围中的数所有都减一,问如何得到最大的 max-min。
题目链接

简单版本直接暴力枚举每个数分别为最大最小,时间复杂度 nnm;

#include<bits/stdc++.h>
#define fi first
#define se second
#define FOR(a) for(int i=0;i<a;i++)
#define sc(a) scanf("%d",&a)
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef pair<P, int> LP;
const ll inf = 1e17 + 10;
const int N = 1e5+10;
const ll mod = 1000000007;
const int base=131;

map<string, int>ml;



int a[N],n,m,t,k,flag,l[N],r[N],cnt;
int vis[N];
vector<int> v,ev;
priority_queue<int> q;



int main()
{


	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++)
	{
		cin>>l[i]>>r[i];
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cnt=a[i]-a[j];
			for(int k=1;k<=m;k++)
			{
				if(j<=r[k]&&j>=l[k])
				{
					if(i<l[k]||i>r[k])
					{
						cnt++;
						v.push_back(k);

					}
				}
			}
			if(ans<cnt)
			{
				ev.clear();
				ans=cnt;
				for(auto x:v) ev.push_back(x);
			}
			v.clear();
		}
	}
	cout<<ans<<endl;
	cout<<ev.size()<<endl;
	for(auto x:ev) cout<<x<<" ";
}

复杂版本:由于n太大,无法进行暴力枚举,看题解才知道解法。
此时枚举每个为最小值,我们要使最后答案最大,所以我们要竟可能减小我们选定的最小值,那么我们可以利用差分数组的思想,保存每一个区间的起始位置和结束位置,然后从1开始枚举最小值位置。那么现在假设1是最小值的位置,我们就应该把包含1的区间全部选中,那么显然,只有以1开始的区间是包含1的,那么我们直接修改数组,把这些区间全部-1。然后再去寻找数组中的最大值,就可以算出1是最小值时的x。

然后枚举到2,因为1开始的区间已经在数组里改过了,那么我们只需要把2开始的区间再全部选中并且-1,那么所有包含2的区间不就都被选中了吗?这时候再寻找数组中的最大值,就可以算出2是最小值时的x。如果有一个区间是从1到1怎么办?枚举到2的时候这个区间是不应该被选的(因为不包含2),所以在判断完位置1之后,应该把所有以1结束的区间重新“退选”,即把这些区间再+1,恢复到没选的状态。这样就保证在枚举i位置为最小值时,当前选中的区间都包含i,没有其他无关区间。

区间修改+区间查询最大值,用线段树维护即可。对于每个区间,只有选择和退选的操作,也就是只修改了2*m次的区间。对于每个位置的i都需要查询最大值。复杂度为(n+2m)*logn,由于m很小,复杂度约为nlogn。

这里借鉴了大佬博客:
https://blog.csdn.net/qq_41643650/article/details/86620128

#include<bits/stdc++.h>
#define fi first
#define se second
#define FOR(a) for(int i=0;i<a;i++)
#define sc(a) scanf("%d",&a)
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
typedef pair<P, int> LP;
const ll inf = 1e17 + 10;
const int N = 1e5+10;
const ll mod = 1000000007;
const int base=131;

map<string, int>ml;



ll a[N],n,m,t,k,flag,l[N],r[N],cnt;
ll lazy[N<<2];
ll tree[N<<2];
vector<int> st[N],en[N],v;
priority_queue<int> q;

void push_up(int rt)
{
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
void push_down(int rt)
{
	if(lazy[rt])
	{
		lazy[rt<<1]+=lazy[rt];
		lazy[rt<<1|1]+=lazy[rt];
		tree[rt<<1]+=lazy[rt];
		tree[rt<<1|1]+=lazy[rt];
		lazy[rt]=0;
	}
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		cin>>tree[rt];
		return ;
	}
	int m=l+r >>1;
	build(rt<<1,l,m);
	build(rt<<1|1,m+1,r);
	push_up(rt);
}

void update(int L,int R,int x,int rt,int l,int r)
{
	if(L<=l&&r<=R)
	{
		tree[rt]+=x;
		lazy[rt]+=x;
		return;
	}
	push_down(rt);
	int m=l+r >>1;
	if(L<=m) update(L,R,x,rt<<1,l,m);
	if(R>m)  update(L,R,x,rt<<1|1,m+1,r);
	push_up(rt);
}

ll query(int L,int R,int rt,int l,int r)
{
	if(L<=l&&r<=R) return tree[rt];
	int m=l+r>>1;
	push_down(rt);
	ll ans=-1e9;
	if(L<=m) ans=max(ans,query(L,R,rt<<1,l,m));
	if(R>m) ans=max(ans,query(L,R,rt<<1|1,m+1,r));
	return ans;
	
}
int main()
{


	ios::sync_with_stdio(false);
	cin.tie(0);

	int x,y;
	cin>>n>>m;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		cin>>x>>y;
		l[i]=x;
		r[i]=y;
		st[x].push_back(y);
		en[y].push_back(x);
	}
	ll res=-inf;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<st[i].size();j++)
			update(i,st[i][j],-1,1,1,n);
		cnt=query(1,n,1,1,n)-query(i,i,1,1,n);
		if(cnt>res)
		{
			res=cnt;
			k=i;
		}
		for(int j=0;j<en[i].size();j++)
			update(en[i][j],i,1,1,1,n);
	}
	//show2("k",k)
	cout<<res<<endl;
	for(int i=1;i<=m;i++)
	{
		if(k>=l[i]&&k<=r[i])
			v.push_back(i);

	}
	cout<<v.size()<<endl;
	for(auto x:v) cout<<x<<" ";


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值