【BZOJ1227】虔诚的墓主人(SDOI2009)-线段树+离散化+组合数

测试地址:虔诚的墓主人
做法:本题需要用到线段树+离散化+组合数。
首先我们可以先将常青树的横纵坐标离散化,可是能成为十字架中心的墓地数量还是可能有W2个,这要怎么办呢?
我们可以从下到上对于每一行,维护十字架中心在这一行的一段连续的墓地中的贡献。注意到我们要求的是:
Cupk×Cdownk×Cleftk×Crightk
在同一段连续的墓地中,leftright都相同,那么我们只需要统计这一段中Cupk×Cdownk的和就可以了。因为我们从下到上枚举行,所以可能会有单点修改的情况,而单点修改区间求和显然可以用线段树或树状数组来做,这里为了练手就写了线段树。
因为点的数量只有W,所以上面的连续段的数量也是W的数量级,至于组合数可以O(Wk)预处理出来,那么总的复杂度就是O(WlogW),可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=(ll)2147483647+(ll)1;
int n,m,w,k,tot0,tot,totx[100010]={0},toty[100010]={0},down[100010]={0};
ll C[100010][11]={0},seg[400010]={0};
struct point
{
    int now,a,b;
}p[100010];

bool cmp1(point a,point b)
{
    if (a.a!=b.a) return a.a<b.a;
    return a.b<b.b;
}

bool cmp2(point a,point b)
{
    if (a.b!=b.b) return a.b<b.b;
    return a.a<b.a;
}

void init()
{
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=w;i++)
        scanf("%d%d",&p[i].a,&p[i].b);
    scanf("%d",&k);

    sort(p+1,p+w+1,cmp1);
    tot0=0;
    for(int i=1;i<=w;i++)
    {
        if (i==1||p[i].a!=p[i-1].a) ++tot0;
        p[i].now=tot0;
    }
    for(int i=1;i<=w;i++)
        p[i].a=p[i].now;

    sort(p+1,p+w+1,cmp2);
    tot=0;
    for(int i=1;i<=w;i++)
    {
        if (i==1||p[i].b!=p[i-1].b) ++tot;
        p[i].now=tot;
    }
    for(int i=1;i<=w;i++)
    {
        p[i].b=p[i].now;
        totx[p[i].a]++,toty[p[i].b]++;
    }

    C[0][0]=1;
    for(int i=1;i<=w;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=k;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
}

void pushup(int no)
{
    seg[no]=seg[no<<1]+seg[no<<1|1];
}

void modify(int no,int l,int r,int x,ll d)
{
    if (l==r) {seg[no]=d;return;}
    int mid=(l+r)>>1;
    if (x<=mid) modify(no<<1,l,mid,x,d);
    else modify(no<<1|1,mid+1,r,x,d);
    pushup(no);
}

ll query(int no,int l,int r,int s,int t)
{
    if (s>t) return 0;
    if (l>=s&&r<=t) return seg[no];
    ll ans=0;
    int mid=(l+r)>>1;
    if (s<=mid) ans=(ans+query(no<<1,l,mid,s,t))%mod;
    if (t>mid) ans=(ans+query(no<<1|1,mid+1,r,s,t))%mod;
    return ans;
}

void work()
{
    ll ans=0;
    int nowx=0;
    for(int i=1;i<=tot;i++)
    {
        int lft=0,last=0;
        for(int j=nowx+1;j<=nowx+toty[i];j++)
        {
            ans=(ans+C[lft][k]*C[toty[i]-lft][k]%mod*query(1,1,tot0,last+1,p[j].a-1)%mod)%mod;
            down[p[j].a]++;
            modify(1,1,tot0,p[j].a,C[down[p[j].a]][k]*C[totx[p[j].a]-down[p[j].a]][k]%mod);
            last=p[j].a;
            lft++;
        }
        nowx+=toty[i];
    }
    printf("%lld",ans);
}

int main()
{
    init();
    work();

    return 0;
}
posted @ 2018-03-19 22:28  Maxwei_wzj  阅读(81)  评论(0编辑  收藏  举报