zoukankan      html  css  js  c++  java
  • 【高手训练】【树状数组】分组

    Description

    Bsny所在的精灵社区有n个居民,每个居民有一定的地位和年龄,ri表示第i个人的地位,ai表示第i个人的年龄。
    最近社区里要举行活动,要求几个人分成一个小组,小组中必须要有一个队长,要成为队长有这样的条件:
    1、队长在小组中的地位应该是最高的(可以并列第一);
    2、小组中其他成员的年龄和队长的年龄差距不能超过K。
    有些人想和自己亲密的人组在同一个小组,同时希望所在的小组人越多越好。比如x和y想在同一个小组,同时希望它们所在的小组人越多越好,当然,它们也必须选一个符合上述要求的队长,那么问你,要同时包含x和y的小组,最多可以组多少人?
    Input

    第一行两个整数n和K;
    接下来一行输入n个整数:r1, r2, …, rn
    接下来一行输入n个整数:a1, a2, …, an
    接下来输入Q表示有Q个询问;
    接下来Q行每行输入x, y,表示询问:当x和y组在同一个小组,它们小组最多可以有多少人(x和y也有可能被选为队长,只要它们符合条件)。
    Output

    对于每个询问,输出相应的答案,每个答案占一行。
    当x和y无法在同一组时,输出-1(比如x的年龄是1, y的年龄是100,K=1,无论谁当队长,x和y两者中,总会有人跟队长的年龄差距超过K,那么输出-1)。
    Sample Input

    5 1
    1 5 4 1 2
    4 4 3 2 2
    4
    5 3
    2 3
    2 5
    4 1
    Sample Output

    4
    3
    -1
    4
    【样例解释】
    询问1:当第5个人和第3个人想在一组时,小组成员可以有{1, 3, 4, 5},选择3当队长,而2不可以加入,因为2加入的话,5和2的年龄差距为2,超过K=1了;
    询问2:当第2个人和第3个人想在一组时,可以选择{1, 2, 3};
    询问3:当2和5想在一起时,无法满足要求;
    询问4:当4和1想在一起时,可以选择{1, 3, 4, 5};
    Data Constraint

    20%的数据:2≤n≤100,0≤ k≤100,1≤ ri, ai ≤100,1≤ q≤ 100;
    40%的数据:2≤ n≤1000,0≤ k≤ 1000,1≤ ri, ai ≤ 1000,1≤ q≤ 1000;
    60%的数据:2≤ n≤ 10^4,0≤ k≤ 10^9,1≤ ri, ai ≤ 10^9, 1≤ q≤ 10^4;
    100%的数据:2≤ n≤ 10^5,0≤ k≤ 10^9,1≤ ri, ai ≤ 10^9,1≤ q≤ 10^5,1≤ x, y≤ n, x≠y。

     其实这道题目一看就觉得很不可做。
    但是仔细想想,不可做的原因其实就是没有条件约束去寻找答案,会成为指数级别的时空复杂度,而且不好写。
    其实是我没看懂题目

    对于40分的暴力,其实就是O(n^2)预处理出来每个人作为队长的时候,能有多少队员,并记录一下是那些。
    如果只是要40分,就可以采用桶的方法记录。
    awa反正没有梦想

    这样就是40分了。
    附上代码

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    struct edge
    {
        int diw,old;
    }e[100001];
    int n,q,k;
    int ans1[1001][1001];
    vector<int> ans[1001];
    inline ll read()
    {
        char c=getchar();ll a=0,b=1;
        for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;
        return a*b;
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
    //    freopen(".out","w",stdout);
        n=read();k=read();
        for(int i=1;i<=n;i++)
        {
            e[i].diw=read();
        }
        for(int i=1;i<=n;i++)
        {
            e[i].old=read();
        }
        for(int i=1;i<=n;i++)//枚举i为队长时的队员个数 
        {
            int now=e[i].old,ndiw=e[i].diw;
            for(int j=1;j<=n;j++)
            {
    //            if(i==j)continue;
                if(e[j].old>=now-k&&e[j].old<=now+k&&e[j].diw<=ndiw)
                {
                    ans[i].push_back(j);
                    ans1[i][j]=1;
                }
            }
        }
        q=read();
        for(int i=1;i<=q;i++)
        {
            int x=read(),y=read();
            int maxx=-1;
            for(int j=1;j<=n;j++)
            {
                if(ans1[j][x]==true&&ans1[j][y]==true)
                {
                    maxx=max(maxx,int(ans[j].size()));
                }    
            }
            if(maxx>0)
            {
                cout<<maxx<<endl;
            }
            else
            {
                cout<<-1<<endl;
            }
        }
        return 0;
    }

    这种方法,其实已经没有了优化的空间(个屁),因为O(n^2)的预处理就已经说明了它必定TLE的事实
    其实可以用树状数组来优化
    其实在想这个问题的时候,就应该注意到这个其实就是有优化的空间的,因为每一个队长都是重头开始枚举的,那么就意味着大量的重复计算
    所以只需要用一些算法来按照一定的顺序枚举,使每一个枚举都能够利用到前面计算过的答案来进行计算就好了。

    awa
    其实这个子问题就是一个二位偏序的模板题。
    怎么说呢?
    每个队长对于队员的限制条件,其实就是年龄和地位。
    其中年龄较为复杂,是两边的限制,所以我们对于地位进行排序。
    这样就可以保证之后每一个枚举到的队长,都能够比前面的人的地位高,然后我们就成功的利用到前面计算过的东西了。


    树状数组的下标代表的其实是年龄。
    但是因为年龄太大了,就离散化一下。awa


    然后每一次就是先给年龄为a[i]的人a[i]+1,再查询区间  [a[i]-k,a[i]+k]  之间的居民个数。
    这就是每个队长的答案。
    但是对于地位一样的这中特殊的情况,我们需要先把他们全部都取出来,加一,再对于他们每一个进行统计。

    这就是这道题目用到了树状数组的全部地方。。。。

    下面其实就是  线段树  了。
    我们发现,如果一个  i  想要成为  x  和  y  的队长,只能是在  i 的地位 比  x  和  y  高的情况下出现,而且  i 与  x  和y的年龄差的最小值不能超过k。
    i  的地位如果是想要不小于  x  和  y  ,那么转化成为式子就是  r[i]>max(r[x],r[y]) 
    所以就能够离线先按x和y的地位的最大值排个序,再进行离线处理。


    然后就是使年龄合法了。
    要使年龄合法,就是要让 队长的年龄在 区间 [max(a[x],a[y])-k,min(a[x],a[y])+k]
    然后就是要求在这个区间的队长的队员的最大值。。。
    然后就可以用 线段树  进行区间最大值的维护。


    时间复杂度是树状数组的O(nlogn)加上线段树的O(nlogn),还有q次查询的O(logn)awa
    所以就是O((n+q)logn)的时间复杂度
    AC!

    code

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int n,k,q,d[101000],tot=1,ans[101000],an,awa=1000000000;
    struct node{
        int x,y,z,l;
    }a[101000],b[101000];
    struct nod1{
        int l,r,d,z;
    }t[2001000];
    inline ll read()
    {
        char c=getchar();ll a=0,b=1;
        for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;
        return a*b;
    }
    bool cnt(node x,node y)
    {
        return x.x<y.x;
    }
    bool cmt(node x,node y)
    {
        return x.z<y.z;
    }
    void add(int v,int i,int j,int x,int y,int z)
    {
        if(z==0)
        {
            t[v].d++;
        }
        else
        {
            t[v].d=max(t[v].d,y);
        }
        if(i==j) return;
        int m=(i+j)/2;
        if(x<=m)
        {
            if(!t[v].l)
            {
                t[v].l=++tot;
            }
        }
        if(x>m)
        {
            if(!t[v].r)
            {
                t[v].r=++tot;
            }
        }
        if(x<=m)
        {
            add(t[v].l,i,m,x,y,z);
        }
        else
        {
            add(t[v].r,m+1,j,x,y,z);
        }
    }
    void get(int v,int i,int j,int x,int y,int z)
    {
        if(x>y) return;
        if(i==x&&j==y)
        {
            if(z==0)
            {
                an+=t[v].d;
            }
            else
            {
                an=max(an,t[v].d);
            }
            return;
        }
        int m=(i+j)/2;
        if(y<=m)
        {
            get(t[v].l,i,m,x,y,z);
        }
        else
        {
            if(x>m)
            {
                get(t[v].r,m+1,j,x,y,z);
            }
            else
            {
                get(t[v].l,i,m,x,m,z),get(t[v].r,m+1,j,m+1,y,z);
            }
        }
    }
    int main()
    {
        freopen("group.in","r",stdin);
        freopen("group.out","w",stdout);
        n=read();k=read();
        for(int i=1;i<=n;i++)
        {
            a[i].x=read();
        }
        for(int i=1;i<=n;i++)
        {
            a[i].y=read();
            a[i].z=i;
        }
        sort(a+1,a+n+1,cnt);
        int j=1;
        for(int i=1;i<=n;i++)
        {
            for(;a[j].x==a[i].x;j++)
            {
                add(1,1,awa,a[j].y,0,0);
            }
            an=0;
            get(1,1,awa,max(a[i].y-k,1),min(a[i].y+k,awa),0);
            d[a[i].z]=an;
        }
        sort(a+1,a+n+1,cmt);
        q=read();
        for(int i=1;i<=q;i++)
        {
            b[i].x=read();b[i].y=read();
            b[i].z=max(a[b[i].x].x,a[b[i].y].x);
            b[i].x=a[b[i].x].y;
            b[i].y=a[b[i].y].y;
            b[i].l=i;
        }
        sort(b+1,b+q+1,cmt);
        sort(a+1,a+n+1,cnt);
        memset(t,0,sizeof(t));
        tot=1;
        int l=q;j=n;
        for(int i=q;i>=1;i--)
        {
            for(;a[j].x>=b[i].z;j--)
            {
                add(1,1,awa,a[j].y,d[a[j].z],1);
            }
            an=-1;
            get(1,1,awa,max(max(b[i].x-k,b[i].y-k),1),min(min(b[i].x+k,b[i].y+k),awa),1);
            ans[b[i].l]=an;
        }
        for(int i=1;i<=q;i++)
        {
            if(ans[i]>0)
            {
                printf("%d
    ",ans[i]);
            }
            else
            {
                printf("-1
    ");
            }
        }
    }
  • 相关阅读:
    二叉平衡树
    红黑树
    [leetcode] LCP 比赛
    二叉搜索树
    面向对象的二叉树的实现
    二叉树的序列化与反序列化
    [leetcode] 基本计算器
    【pandas】玩转一行拆多行,多行并一行(分分合合你说了算)
    【VBA】数据溢出与解决
    【VBA】criterial 未找到命名参数
  • 原文地址:https://www.cnblogs.com/HLZZPawa/p/13049740.html
Copyright © 2011-2022 走看看