【BZOJ 1227】 [SDOI2009]虔诚的墓主人

一贯的链接: http://dev.luogu.org:8888/wiki/show?name=%E9%A2%98%E8%A7%A3+P2154

又是神题!!!!

注意 他说自然溢出! 坑死我了啊 自然溢出会变负数!!! 还要自己加上MO啊!!!

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define MO 2147483648
struct H
{
    int x,y;
    int UP,DOWN;  //这个点上面有多少棵树(包括这个点) ,这个点下面有多少棵树(不包括这个点) 
    int id;
}a[100000+1+1];
bool cmpx(H a,H b){ if(a.x==b.x) return a.y<b.y;return a.x<b.x;}
bool cmpy(H a,H b){ if(a.y==b.y) return a.x<b.x;return a.y<b.y;}
int n,m,w,k;
int s[100000+1],id[100000+1],cnt=0;  //树状数组 离散 
int c[100000+1][10+1];  //组合 
int R[100000+1];  //每行 树的数量 
int lowbit(int x){return x&-x;}
int Sum(int x)
{
//  cout<<x<<endl;
    int ans=0;
    while(x>=1)
        ans+=s[x],x-=lowbit(x);
    return ans; 
}
void Add(int x,int d)
{
    while(x<=cnt)
        s[x]+=d,x+=lowbit(x);
}
int main()
{
    scanf("%d %d %d",&n,&m,&w);
    for(int i=1;i<=w;i++) scanf("%d %d",&a[i].x,&a[i].y);
    a[0].x=a[0].y=a[w+1].x=a[w+1].y=-1;
    scanf("%d",&k);

    //组合
    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];
    }   
    //离散  x  id[] 
    //处理 每颗树 上下 各有多少棵树 
    sort(a+1,a+w+1,cmpx);
    for(int i=1;i<=w;i++) if(a[i].x==a[i-1].x) a[i].DOWN=a[i-1].DOWN+1,a[i].id=cnt; else a[i].id=++cnt;
    for(int i=w;i>=1;i--) if(a[i].x==a[i+1].x) a[i].UP=a[i+1].UP+1;else a[i].UP=1;
    // 每一行有多少颗树 
    int tmp=0;
    sort(a+1,a+w+1,cmpy); 
    for(int i=1;i<=w;i++)
    {
        tmp++; 
        if(a[i].y!=a[i+1].y) R[i]=tmp,tmp=0;
    }
    for(int i=w;i>=1;i--) if(a[i].y==a[i+1].y) R[i]=R[i+1];
    //求和 
    tmp=0;
    int ans=0;
    for(int i=1;i<=w;i++)
    {
        tmp++; 
        if(a[i].y!=a[i+1].y)    
            tmp=0;
        else
            ans+=(c[tmp][k]*c[ R[i]-tmp ][k])*(Sum(a[i+1].id-1)-Sum(a[i].id));
        Add(a[i].id,  - c[a[i].UP][k] * c[a[i].DOWN][k] + c[a[i].UP-1][k] * c[a[i].DOWN+1][k] );
    }
    while(ans<0) ans+=MO;  //自然溢出会变负数!!!!! 
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值