(AtCoder Beginner Contest 346) (G 扫描线+数组转化成二维)

对于某个数

如果左边区间是[l,i]

右边区间是[i,r]

正常是 两个区间长度相乘,但是这个题目会重复

所以问题转化成一个二维坐标

x轴是L,y轴是R

那么就变成一个矩阵,左下角是(l,i)右上角是(i,r)

对于矩阵每个点都是合法的,所以变成扫描线求不重复的点对

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10,M=2*N,mod=1e9+7;
#define int long long
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
using Node=tuple<int,int,int>;
const long long inf=1e18;

int n;
int a[N];
int l[N],r[N];

struct segment{
    int x,y1,y2;
    int d;
    bool operator<(const segment&t) const{
        return x<t.x;
    }
}seg[N*10];
struct node{
    int l,r;
    int cnt,len;
}tr[N*10];

void pushup(int u)
{
    if(tr[u].cnt)
    {
        tr[u].len=tr[u].r-tr[u].l+1;
    }
    else if(tr[u].cnt==0&&tr[u].l!=tr[u].r)
    {
        tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
    }
    else 
    {
        tr[u].len=0;
    }
}
void build(int u,int l,int r){
    tr[u]={l,r,0,0};
    if(l==r) return ;
    else{
        int mid=l+r>>1;
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
    }
}
void modify(int u,int l,int r,int d)
{
    if(tr[u].l>=l&&tr[u].r<=r){
        tr[u].cnt+=d;
        pushup(u);
    }else{
        int mid=tr[u].r+tr[u].l>>1;
        if(l<=mid) modify(u<<1,l,r,d);
        if(r>mid) modify(u<<1|1,l,r,d);
        pushup(u);
    }
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    unordered_map<int,int> mp;
    for(int i=1;i<=n;i++){
        if(mp.count(a[i])) l[i]=mp[a[i]]+1;
        else l[i]=1;
        mp[a[i]]=i;
    }
    mp.clear();
    for(int i=n;i>=1;i--){
        if(mp.count(a[i])) r[i]=mp[a[i]]-1;
        else r[i]=n;
        mp[a[i]]=i;
    }
    int j=0;
    int res=0;
    map<int,vector<Node>> g;
    for(int i=1;i<=n;i++)
    {
        int x1=l[i],y1=i,x2=i,y2=r[i];
        g[x1].emplace_back(y1,y2,1);
        g[x2+1].emplace_back(y1,y2,-1);

    }
    sort(seg,seg+j);

    int m=200000;
    build(1,1,m);

    int lst=-1;
    for(auto [k,v]:g)
    {     if(lst!=-1) res+=tr[1].len*(k-lst);
        for(auto [y1,y2,d]:v)
        {
            modify(1,y1,y2,d);
        }
     //   cout<<k<<" "<<tr[1].len<<"\n";
   
        lst=k;    
    }
    
    cout<<res;
}
//1 2 3 4
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;

    //cin>>t;

    while(t--) solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值