2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分

题目链接:https://ac.nowcoder.com/acm/contest/903/E
题目大意:

有n个男孩,索引从1到n,n个女孩索引从n+1到2n。

有一天,他们在一起开派对。女孩们坐在第一排,男孩们坐在第二排。他们的坐姿是这样的:一个男孩坐在一个女孩的后面,一个男孩坐在最左边的椅子上,另一个男孩坐在第二个椅子上,等等。

每个男孩都有一个他喜欢的女孩,他可以通过在一张纸上写下他想对她说的话来和他的爱人取得联系,并把它做成一个纸飞机,他会直接朝她扔去。你可以假设飞机会直线飞行。

但是,当两个纸飞机在空中相撞并中途坠落时,问题就出现了。显然,这将是非常尴尬的。所以每个男孩都想知道,如果他扔纸飞机,有多少纸飞机有可能与它相撞。

每个女孩都是一个男孩的心上人。

输入:
第一行包含一个整数n。

接下来是n行,第i行包含两个整数,第一个是坐在第i个男孩前面的女孩和他喜欢的女孩的索引。1≤n≤10^5

输出n行,

其中i-th包含一个整数,可能与i个男孩碰撞的飞机数。
2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分_第1张图片
思路:
2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分_第2张图片
我们把右边的点也记做1-n

2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分_第3张图片
2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分_第4张图片

所有可以用主席树维护。每次把b[i]加进去。
查询[i, n]与[1,b[i]]匹配的数量:
cx(root[i-1], 1, n, b[i]+1, n)
查询[0, i]与[b[i]+1, n]匹配的数量:
cx(root[n], 1, n, 1, b[i]-1)-cx(root[i], 1, n, 1, b[i]-1);

还可以用树状数组写,我当时没有想到,也是用差分的思想,本来省赛还补过,也是因为这题才学的主席树:https://blog.csdn.net/qq_21433411/article/details/89528291。

因为这个查询是固定的所以可以边添加边查询,记录下来就ok了。
2019河北省大学生程序设计竞赛(重现赛)- E 主席树or数状数组差分_第5张图片

for(int i=n;i>=1;i--)//查询0-b[i] -> 1的和就是b[i]+1 -> n与1-b[i] -> 1匹配的数量
{
    L[i]=cx(mp[b[i]]-1);
    add(mp[b[i]], 1);
}
for(int i=1;i<=n;i++)//查询b[i]+1 -> n的和就是1 -> a[i]-1与b[i]+1 -> n匹配的数量。
 {
        R[i]=cx(n)-cx(mp[b[i]]);
        add(mp[b[i]], 1);
 }
//树状数组
#include
#define LL long long
using namespace std;

int n, x, sum[50005];
int a[50005], b[50005];
int L[50005]={0}, R[50005]={0};
map<int, int> mp;

void add(int p, int y)//p位置+y
{
    while(p<=n)
    {
        sum[p]+=y;
        p+=(p&-p);
    }
}

int cx(int p)         //前缀和
{
    int ans=0;
    while(p)
    {
        ans+=sum[p];
        p-=(p&-p);
    }
    return ans;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        mp[a[i]]=i;
    }
    memset(sum, 0, sizeof(sum));
    for(int i=n;i>=1;i--)
    {
        L[i]=cx(mp[b[i]]-1);
        add(mp[b[i]], 1);
    }

    memset(sum, 0, sizeof(sum));
    for(int i=1;i<=n;i++)
    {
        R[i]=cx(n)-cx(mp[b[i]]);
        add(mp[b[i]], 1);
    }
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",L[i]+R[i]);
    }

    return 0;
}
//主席树
#include

#define LL long Long
using namespace std;
const int maxn=2e5+10;

int L[maxn*20], R[maxn*20], sum[maxn*20];
int n, q, m ,cnt = 0;
int a[maxn], b[maxn], root[maxn];
map<int ,int> mp;

#define mid ((l+r)>>1)
int JS(int l, int r)
{
    int rt=++cnt;
    sum[rt]=0;
    if(l<r)
    {
        L[rt]=JS(l ,mid);
        R[rt]=JS(mid+1, r);
    }
    return rt;
}

int gx(int i, int l, int r, int x)
{
    int rt=++cnt;
    L[rt]=L[i], R[rt]=R[i], sum[rt]=sum[i]+1;//更新
    if(l<r)
    {
        if(x<=mid)
        {
            L[rt]=gx(L[i], l, mid, x);
        }
        else
        {
            R[rt]=gx(R[i], mid+1, r, x);
        }
    }
    return rt;
}

int ans=0;
int cx(int i, int l, int r, int L1, int R1)
{
    if(R1<L1)
    {
        return 0;
    }
    if(l==L1&&r==R1)
    {
        ans+=sum[i];
        return 0;
    }

    if(R1<=mid)
    {
        cx(L[i], l, mid, L1, R1);
    }
    else if(L1>=(mid)+1)
    {
        cx(R[i], (mid)+1, r, L1, R1);
    }
    else
    {
        cx(L[i], l, mid, L1, mid);
        cx(R[i], (mid)+1, r, (mid)+1, R1);
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i], &b[i]);
        mp[a[i]]=i;
    }
    for(int i=1;i<=n;i++)
    {
        b[i]=mp[b[i]];
    }
    root[0] = JS(1, n);//建空树

    for(int i=1;i<=n;i++)
    {
        //cout<
        root[i]=gx(root[i-1], 1, n, b[i]);//更新
    }
    for(int i=1;i<=n;i++)
    {
        int s=0;
        ans=0;
        cx(root[i-1], 1, n, b[i]+1, n);
        s+=ans;

        ans=0;
        cx(root[n], 1, n, 1, b[i]-1);
        s+=ans;

        ans=0;
        cx(root[i], 1, n, 1, b[i]-1);
        s-=ans;

        printf("%d\n",s);
    }

    return 0;
}

你可能感兴趣的:(主席树,树状数组)