2023牛客暑期多校训练营9 -I Non-Puzzle: Segment Pair

2023牛客暑期多校训练营9 -I Non-Puzzle: Segment Pair

炒鸡棒棒糖 给你传送的力量ang↑→~

题目描述

在这里插入图片描述

( 1 ≤ n ≤ 5 ⋅ 1 0 5 ) , ( 1 ≤ l i ≤ r i ≤ 5 ⋅ 1 0 5 , 1 ≤ l i ′ ≤ r i ′ ≤ 5 ⋅ 1 0 5 ) (1\le n\le 5·10^5),(1\le l_i \le r_i \le5·10^5,1\le l_i^{'} \le r_i^{'} \le5·10^5) (1n5105),(1liri5105,1liri5105)

解题思路

本题中有区间的计算同时n的大小也只有5000,所以我们可以想到差分。
一开始,会想到枚举每一对线段在继续查询,但显然这个复杂度不对,是 2 n 2^n 2n 所以我们另辟蹊径。
因为一对线段重叠,则统计的方案数 ∗ 2 *2 2所以可以先用差分标记读进来的两条线段, c [ l ] + + , c [ r + 1 ] − − c[l]++,c[r+1]-- c[l]++,c[r+1]如果右区间的左端点 在左区间的右端点 右侧则中间的这一段区间 它的贡献值为 ∗ 2 *2 2。同时有贡献的区域就是 [ + 1 ] 个数 − [ + 2 ] 为 n [+1]个数-[+2]为n [+1]个数[+2]n的区间,加上 2 [ + 2 ] 的点个数 2^{[+2]的点个数} 2[+2]的点个数。由于我们差分标记的是点而不是区间,所以一段区间内我们重复算了许多次,需要最后删除。

代码

#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=5e5+4;
#define ll long long 
ll q(int x,int r)  快速幂-其实也可以直接预处理
{
	int res=1;
	while(r)
	{
		if(r&1) res=1ll*res*x%mod;
		x=1ll*x*x%mod;
		r>>=1;
	}
	return res;
}
vector<pair<int,int>> ca[N],rep[N];  ca是点的差分,rep是需要删除的重复计算的差分(就是点的右端点左移
int n;
int main()
{
	scanf("%d",&n);
	int l1,r1,l2,r2;
    int bg=N,la=0;  找到点大小的左右值域
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        bg=min(bg,min(l1,l2));
        la=max(la,max(r1,r2));
		ca[l1].push_back({1,0});    差分
        ca[l2].push_back({1,0});
        ca[r1+1].push_back({-1,0});
        ca[r2+1].push_back({-1,0});
        int lf=max(l1,l2),rg=min(r1,r2);  如果有重叠部分
        if(lf<=rg) 
        {
            ca[lf].push_back({0,1});   将 second 赋值
            ca[rg+1].push_back({0,-1});   
        }
        r1--,r2--;       右端点左移 ,只留下一个点
        rep[l1].push_back({1,0});
        rep[l2].push_back({1,0});
        rep[r1+1].push_back({-1,0});
        rep[r2+1].push_back({-1,0});
        rg--;
        if(lf<=rg) 
        {
            rep[lf].push_back({0,1});
            rep[rg+1].push_back({0,-1});   
        }
	}
    int cnt1=0,cnt2=0,cnt3=0,cnt4=0;
    ll ans1=0,ans2=0;
    for(int i=bg;i<=la;i++)
    {
        for(auto j:ca[i])
        {
            cnt1+=j.first; 有值的
            cnt2+=j.second;  重叠部分
        }
        if(cnt1-cnt2==n)  若n对线段包含的
            ans1=(ans1+q(2,cnt2))%mod;
        for(auto j:rep[i])
        {
            cnt3+=j.first;
            cnt4+=j.second;
        }
        if(cnt3-cnt4==n)
            ans2=(ans2+q(2,cnt4))%mod;
    }
    printf("%lld",((ans1-ans2)%mod+mod)%mod);
 	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值