题解【CF1324D Pair of Topics】

\[ \texttt{Description} \]

给两个长度为 \(n\) 的序列 \(a\)\(b\) ,求满足 \(a_i+a_j>b_i+b_j \ (i<j)\) 的数对个数。
\[ \texttt{Solution} \]

  • 首先注意到这个式子左右两边同时出现了 \(i,j\) ,也就是说式子两边不是互相独立的。
  • 嗯,自然地想到了移项大法,式子可以化为 \(a_i-b_i>b_j-a_j \ (i<j)\) 。式子的一边为 \(i\) ,一边为 \(j\) ,就使得式子两边互相独立。
  • 我们发现这个偏序问题简直不要太好做,有很多的方法可以解决这个偏序问题,这里介绍一种 动态开点权值线段树 的做法。
  • 倒序扫描,从 \(n\) 扫到 \(1\)
    1. 将权值线段树中值域在 \([-\inf,a_i-b_i-1]\) 的数的个数计入答案中。
    2. 在权值线段树中令 \(b_i-a_i\) 的个数加一。
  • 时间复杂度 \(\mathcal{O(n \log n)}\) ,空间复杂度 \(O(n \log size)\) ,如果离散化一下可以做到 \(O(n \log n)\)

\[ \texttt{Code} \]

#include<cstdio>

#define RI register int

using namespace std;

namespace IO
{
    static char buf[1<<20],*fs,*ft;
    inline char gc()
    {
        if(fs==ft)
        {
            ft=(fs=buf)+fread(buf,1,1<<20,stdin);
            if(fs==ft)return EOF;
        }
        return *fs++;
    }
    #define gc() getchar()
    inline int read()
    {
        int x=0,f=1;char s=gc();
        while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
        return x*f;
    }
}using IO::read;

const int N=200100,MLOGN=10001000;

int n;

int a[N],b[N];

const int INF=2e9;

int tot,root;
struct SegmentTree{
    int lc,rc;
    int cnt;
}t[MLOGN];

int New()
{
    tot++;
    t[tot].lc=t[tot].rc=t[tot].cnt=0;
    return tot;
}

void insert(int &p,int l,int r,int delta,int val)
{
    if(!p)
        p=New();
    t[p].cnt+=val;
    if(l==r)return;
    int mid=(long long)(l+r)>>1;
    if(delta<=mid)
        insert(t[p].lc,l,mid,delta,val);
    else
        insert(t[p].rc,mid+1,r,delta,val); 
}

int ask(int p,int l,int r,int s,int e)
{
    if(!p)
        return 0;
    if(s<=l&&r<=e)
        return t[p].cnt;
    int mid=(long long)(l+r)>>1;
    int val=0;
    if(s<=mid)
        val+=ask(t[p].lc,l,mid,s,e);
    if(mid<e)
        val+=ask(t[p].rc,mid+1,r,s,e);
    return val;
}

long long ans;

int main()
{
    n=read();

    for(RI i=1;i<=n;i++)
        a[i]=read();

    for(RI i=1;i<=n;i++)
        b[i]=read();

    for(RI i=n;i>=1;i--)
    {
        ans+=ask(root,-INF,INF,-INF,a[i]-b[i]-1);
        insert(root,-INF,INF,b[i]-a[i],1);
    }

    printf("%lld\n",ans);

    return 0;
}

\[ \texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]

猜你喜欢

转载自www.cnblogs.com/cjtcalc/p/12484127.html