The 18th Northeast Collegiate Programming Contest G

补题链接
看网上好像不怎么找不到代码,发一份出来

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
using i128 = __int128;

constexpr int maxn = 1e5+10,k = 316;
int n,m,a[maxn],f[maxn];
vector<int> pos[maxn];
i64 pre[320][maxn];

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>n>>m;
    for(int i = 1;i<=n;++i){
        cin>>a[i];
        pos[a[i]].emplace_back(i);
    }
    for(int i = 1;i<=n;++i){
        if(pos[i].size()>k){
            f[0]++;
            f[i] = f[0];
            for(int j = 1;j<=n;++j){
                pre[f[i]][j] = pre[f[i]][j-1]+(a[j]==i);
            }
            for(int j = 1;j<=n;++j){
                int lst_p =0;
                for(int p:pos[j]){
                    pre[f[i]][p] = pre[f[i]][p]+pre[f[i]][lst_p];//真正有用位置的前缀和,二次累加
                    lst_p = p;
                }
            }
        }
    }
    for(int i = 1;i<=n;++i) pos[i].emplace_back(n+10);
    for(int qry=1;qry<=m;++qry){
        int l,r,p,q;//题目保证p<q
        cin>>l>>r>>p>>q;
        if(pos[p].size()<=k+1&&pos[q].size()<=k+1){
            int pl=0,np=0,nq=0,nn=0;//np,nq记录p,q的指针指到哪里了
            i64 ans =0;
            while(np<pos[p].size()||nq<pos[q].size()){
                if(np==pos[p].size()){
                    pl = pos[q][nq];
                    nq++;
                }else if(nq==pos[q].size()){
                    pl = pos[p][np];
                    np++;
                }else if(pos[q][nq]<pos[p][np]){
                    pl = pos[q][nq];
                    ++nq;
                }else{
                    pl = pos[p][np];
                    np++;
                }
                if(l<=pl&&pl<=r){
                    if(a[pl]==q) ++nn;
                    else ans+=nn;//累加每个q的贡献
                }
            }
            cout<<ans<<"\n";
        }else{
            if(pos[p].size()<pos[q].size()) swap(p,q);//可能导致求成顺序对
            i64 ans =0 ;
            int pl = lower_bound(pos[p].begin(),pos[p].end(),l)-pos[p].begin();
            int pr = upper_bound(pos[p].begin(),pos[p].end(),r)-pos[p].begin();
            pr--;pl--;//相当于[l-1,r]

            int ql = lower_bound(pos[q].begin(),pos[q].end(),l)-pos[q].begin();
            int qr = upper_bound(pos[q].begin(),pos[q].end(),r)-pos[q].begin();
            qr--;ql--;

            i64 nn = pr-pl;
            i64 nnq = qr-ql;
            if(nn>0&&nnq>0){
                if(qr>=0) qr = pos[q][qr];
                else qr =0;
                if(ql>=0) ql = pos[q][ql];
                else ql=0;

                if(ql<=qr) ans+=(pre[f[p]][qr]-pre[f[p]][ql])-nnq*(pl+1);
                if(p<q) ans = nn*nnq-ans;
            }
            cout<<ans<<"\n";
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值