CodeCraft-22 and Codeforces Round #795 (Div. 2)D,E

D.Max GEQ Sum

题意:
给你长度为 n ( 1 ≤ 2 ∗ 1 0 5 ) n(1 \leq 2*10^5) n(12105)的数组 a a a,是否满足对于任何 i , j ( 1 ≤ i ≤ j ≤ n ) i,j(1 \leq i \leq j \leq n) i,j(1ijn),保证
max ⁡ ( a i , a i + 1 , . . . , a j − 1 , a j ) ≥ a i + a i + 1 + . . . + a j 1 + a j \max(a_i,a_{i+1},...,a_{j-1},a_j) \geq a_i+a_{i+1}+...+a_{j_1}+a_j max(ai,ai+1,...,aj1,aj)ai+ai+1+...+aj1+aj思路:
通过单调栈,我们可以找到一个数字左/右第一个比它大的数字的位置,所以我们可以知道最大值为 a i a_i ai时的左右边界的取值范围
在这个范围内,只要 ∑ a \sum a a的最大值小于 max ⁡ a \max a maxa即满足要求,所以记录一个前缀和,并用线段树维护区间最大值和最小值,用右侧最大的值减去左侧最小的值算出的即为最大的 ∑ a \sum a a

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
int n,a[maxn],b[maxn],l[maxn],r[maxn];
struct tree
{
	int l,r,mx,mn;
	int mid()
	{
		return (l+r)/2;
	}
}tre[maxn<<2];
void pushup(int num)
{
    tre[num].mx=max(tre[2*num].mx,tre[2*num+1].mx);
    tre[num].mn=min(tre[2*num].mn,tre[2*num+1].mn);
}
void build(int num,int l,int r)
{
	tre[num].l=l,tre[num].r=r;
	if(l==r)
	{
		tre[num].mx=tre[num].mn=a[l];return;
	}
	int mid=tre[num].mid();
	build(2*num,l,mid);
	build(2*num+1,mid+1,r);
    pushup(num);
}
int query_max(int num,int l,int r)
{
	if(tre[num].l==l && tre[num].r==r)
	return tre[num].mx;
	int mid=tre[num].mid();
	if(l>mid)
	return query_max(2*num+1,l,r);
	else if(r<=mid)
	return query_max(2*num,l,r);
	else
	return max(query_max(2*num,l,mid),query_max(2*num+1,mid+1,r));
}
int query_min(int num,int l,int r)
{
	if(tre[num].l==l && tre[num].r==r)
	return tre[num].mn;
	int mid=tre[num].mid();
	if(l>mid)
	return query_min(2*num+1,l,r);
	else if(r<=mid)
	return query_min(2*num,l,r);
	else
	return min(query_min(2*num,l,mid),query_min(2*num+1,mid+1,r));
}
signed main()
{
	int T;scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&b[i]);
		}
		stack<int>s;
		for(int i=1;i<=n;i++)
		{
			while(!s.empty() && b[i]>=b[s.top()]) s.pop();
			if(s.size()==0) l[i]=0;
			else l[i]=s.top();
			s.push(i);
		}
		while(!s.empty()) s.pop();
		for(int i=n;i>=1;i--)
		{
			while(!s.empty() && b[s.top()]<=b[i]) s.pop();
			if(s.size()==0) r[i]=n+1;
			else r[i]=s.top();
			s.push(i);
		}
		for(int i=1;i<=n;i++) a[i]=a[i-1]+b[i];
		a[n+1]=a[n];
		build(1,0,n+1);
		
		int ok=1;
		for(int i=1;i<=n;i++)
		{
			int mn=query_min(1,l[i],i-1);
			int mx=query_max(1,i,r[i]-1);
			if(mx-mn>b[i])
			{
				ok=0;break;
			}
		}
		ok?puts("YES"):puts("NO");
	}
}

E. Number of Groups

题意:
给你 n n n段区间,每个区间有颜色 0 , 1 0,1 0,1,和长度 l , r ( 0 ≤ l i ≤ r i ≤ 1 0 9 ) l,r(0≤l_i≤r_i≤10^9) l,r(0liri109)
如果两段区间颜色不同,且有交点则认为可以用链子给他们连接起来,问这 n n n个区间会形成多少个集合
在这里插入图片描述
思路:
很明显的并查集题目,问题在于如何优化一些时间
个人的一个思路是:将所有区间左右拆开进行排序,用 s e t set set存储,如果是左端点,并将另一个颜色的 s e t set set与之连接,则放入 s e t set set,否则从中删除。
考虑到速度过慢的原因是因为反复连接了一些已经在同一个集合中的边,所以我们将已经连接在一起的同色区间删除,只保留右端点最远的一个

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
int fa[maxn],n;
int find(int x){if(x==fa[x]) return fa[x]; return fa[x]=find(fa[x]);}
void lianjie(int x,int y){int px=find(x),py=find(y);fa[px]=py;}
struct node
{
    int l,r,id;
    bool operator < (const node& n1)const
    {
    	if(n1.r==r && n1.l==l) return n1.id>id;
    	if(n1.r==r) return n1.l>l;
    	return n1.r>r;
    }
}a[maxn];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);
        vector<tuple<int,int,int,int,int>>v;
        for(int i=1;i<=n;i++)
        {
            int col,l,r;
            fa[i]=i;scanf("%d%d%d",&col,&l,&r);
            v.push_back({l,1,r,col,i});v.push_back({r,2,l,col,i});
        }
        sort(v.begin(),v.end());
        multiset<node>s[2];
        for(int i=0;i<v.size();i++)
        {
            vector<tuple<int,int,int,int>>del;
            auto [x,y,z,col,id]=v[i];
            if(y==1)
            {
                for(auto [l,r,id2]:s[col^1])
                {
                    if(x<=r)
                    {
                        del.push_back({l,r,id2,col^1});
                        lianjie(id,id2);
                    }
                }
                if(del.size()) del.pop_back();
                s[col].insert({x,z,id});
            }
            else
            {
                del.push_back({z,x,id,col});
                // printf("---\n");
            }
            for(auto [x,y,z,c]:del)
            {
                s[c].erase({x,y,z});
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++) if(fa[i]==i) ans++;//printf("i=%d \n",i);
        printf("   %d\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值