Codeforces Round 873 (Div. 2)

Problem - D2 - Codeforces

思路:

  1. 我们先考虑D1的暴力做法,如何求解[l,r]最优?
    1. 首先,显然的,我们独立排序的每一段区间不重叠,否则他们合并一起来排序代价也是一样的,无需分开。
    2. 发现[l,r]我们每能够独立分开一段,代价-1。
    3. 所以我们的思路转化为如何尽可能让他们的分开——设[l,r]分开为[l,k],[k+1,r]。需要满足max(l~k)<min(k+1,r)。否则他们必须一起排序。
    4. 我们暴力的话当然可以在固定i,枚举i~n时存入当前已经分开的区间,如果后面出现x小于已有区间的max,显然要合并到一个区间(可能不止合并一个,会把原来的几个区间合并一起)。
    5. 这样O(n^2)可以解决
  2. D2时,我们有一个明确思路就是继承max(l~k)<min(k+1,r)。
  3. 那么我是否可以枚举a[i]最小然后logn查询符合上述条件的最小l与最大r呢。
  4. 再仔细思考一下,我们实际要枚举a[i],找到满足a[i]=min(k+1,r)的边界(k,y),即k是i左边第一个a[k]<a[i],y是i右边第一个a[y]<a[i],那么[k+1,y-1]就是符合区间,那么我们还要找k+1左边的第一个大于a[i]的数下标x,那么[x+1,k]就是上述的max区间
  5. 处理a[i]左右最大最小,我们可以用单调栈解决
  6. 寻找k+1左边第一个大于a[i]的数。
    1. 如果我们的a[i]是从大处理到小,然后处理完的数的下标就扔到集合里面,那么我们查询下标小于k(a[k]小于a[i])并且还要满足大于a[i]的数,是不是直接在这个集合找即可(找下标)。
    2. 所以我们开个数组排序(大到小),然后从大的数处理到小的数即可
  7. 显然可以减少的贡献是(y-i)*(k-x)
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 3e5 + 10;
int a[N],b[N];
void mysolve()
{
	int n;
	cin>>n;
	int ans=0;
	for(int i=1; i<=n; ++i)cin>>a[i],b[i]=i,ans+=i*(i-1)/2;
	sort(b+1,b+1+n,[&](int x,int y)//从大到小排序
	{
		return a[x]>a[y];
	});
	vector<int>l(n+1),r(n+1,n+1);
	stack<pii>s;
	for(int i=1; i<=n; ++i)//单调栈处理左右最小
		{
			while(!s.empty()&&s.top().first>a[i])r[s.top().second]=i,s.pop();
			if(!s.empty())l[i]=s.top().second;
			s.push({a[i],i});
		}
	set<int>st;
	st.insert(0);
	for(int i=1; i<=n; ++i)
		{
			int p=b[i];
			int k=l[p],y=r[p];
			int x=k?*prev(st.lower_bound(k)):0;//找到集合下标小于x的元素(找不到有一开始插入的0垫着)
			ans-=(y-p)*(k-x);//注意,这里p才是实际的下标,i不是
			st.insert(p);//大到小处理,处理完的数的下标扔到集合
		}
	cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值