1. 程式人生 > >2015年ACM/ICPC瀋陽賽區 I題(二維樹狀陣列)

2015年ACM/ICPC瀋陽賽區 I題(二維樹狀陣列)

題意:給你n(n<=1e5)個正整數二元組<a,b> (1<=a,b<=1e5)

給你m(m<=1e5)個正整數三元組<c,d,e>(1<=c,d<=1e3  1<=e<=1e5)

定義新的三元組等於上述兩個二元組的“積”,運算規則如下:

<a,b> * <c,d,e> =<a,c,d>  if b==e

求新的所有三元組中,有多少個三元組在凸點.(沒有其它三元組比它大)

思路:本題的關鍵是資料範圍,注意我紅色標記的部分。

注意到以後,求凸點就比較容易想到二維樹狀陣列了。但是直接暴力相乘兩個二元組的話會爆炸。

注意到新的三元組的定義。也就是說二元組中b相同的,只有最大的a才會對答案有貢獻。(想想是不是)

因此對於每個b,只保留最大的a。相同的累加數量。

這樣和三元組做“積”之後,新的三元組的數量不會超過1e5。然後合併重複的三元組。

然後就是二維樹狀陣列求凸點的操作了。

按a從小到大排序,相同b小的排前面,再相同c小的排前面。

然後倒著往二維樹狀數組裡加點(b,c),如果它右上角有比他大的點,則這個新三元組對答案沒貢獻。

注意long long。

程式碼:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
const int maxn=1010;
const int maxm=100010;
ll C[maxn][maxn];
int n,m;
int mp[maxm];
ll num[maxm];
struct node
{
    int a,b,c;
    ll sum;
    node(){}
    node(int aa,int bb,int cc,ll dd)
    {
        a=aa;b=bb;c=cc;sum=dd;
    }
    bool operator<(node aa)const
    {
        if(aa.a!=a) return a<aa.a;
        else if(aa.b!=b) return b<aa.b;
        else return c<aa.c;
    }
    node operator +(const node &aa)const
    {
        return node(a,b,c,sum+aa.sum);
    }
    bool operator ==(const node &aa)const
    {
        return (!(*this<aa||aa<*this));
    }
}st[maxm];
int lb(int x){return x&(-x);}
void add(int x,int y,ll v)
{
    for(int i=x;i<maxn;i+=lb(i))
    for(int j=y;j<maxn;j+=lb(j))
    C[i][j]+=v;
}
ll query(int x,int y)
{
    ll ans=0;
    for(int i=x;i>0;i-=lb(i))
    for(int j=y;j>0;j-=lb(j))
    {
        ans+=C[i][j];
    }
    return ans;
}
int main()
{
    int T,cas=1;
    scanf("%d",&T);
    int x,y;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        memset(C,0,sizeof(C));
        memset(mp,0,sizeof(mp));
        memset(num,0,sizeof(num));
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            if(mp[y]<x) {mp[y]=x;num[y]=1;}
            else if(mp[y]==x){num[y]++;}
        }
        int cnt=0;
        for(int i=0;i<m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(mp[z]) st[cnt++]=node(mp[z],x,y,num[z]);
        }
        sort(st,st+cnt);
        int tot=0;
        for(int i=1;i<cnt;i++)
        {
            if(st[tot]==st[i]) st[tot]=st[tot]+st[i];
            else st[++tot]=st[i];
        }
        ll ans=0;
        for(int i=tot;i>=0;i--)
        {
            int x=st[i].b,y=st[i].c;
            ll f=query(1000,1000)-query(x-1,1000)-query(1000,y-1)+query(x-1,y-1);
            if(!f) ans+=st[i].sum;
            add(x,y,1);
        }
        printf("Case #%d: %lld\n",cas++,ans);
    }
    return 0;
}