[杂题 离散 扫描线] BZOJ1227: [SDOI2009]虔诚的墓主人

不难的题。
考虑一个空的点的贡献是一个组合数的形式,即答案为

(leftK)(rightK)(upK)(downK)

怎么求呢? 显然先离散,只需考虑离散后的网格上的点。
对于每一行,可以考虑扫过取求两个实点中间空点的贡献,每一段的贡献是
(leftK)(rightK)(upK)(downK)

前面两项是不变的,所以我们需要维护每一列当前的形如 (upK)(downK) 的贡献,支持查询区间和。树状数组即可。

#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL; 
const LL maxn=200005,MOD=2147483648LL;
struct data{ int x,y; } a[maxn];
vector<int> r[maxn];
int n,K,cntAll[maxn],cntNow[maxn],b1[maxn],b2[maxn];
LL C[maxn][13],ans;
int ID1(int x){ return lower_bound(b1+1,b1+1+b1[0],x)-b1; }
int ID2(int x){ return lower_bound(b2+1,b2+1+b2[0],x)-b2; } 
LL bit[maxn];
void Update(int x,LL val){
    for(val%=MOD;x<=b2[0];x+=(x&(-x))) (bit[x]+=val)%=MOD;
}
LL Query(int x){
    LL res=0;
    for(;x;x-=(x&(-x))) (res+=bit[x])%=MOD;
    return res;
}
int main(){
    freopen("bzoj1227.in","r",stdin);
    freopen("bzoj1227.out","w",stdout);
    scanf("%*d%*d%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
        b1[++b1[0]]=a[i].x; b2[++b2[0]]=a[i].y;
    }
    scanf("%d",&K);
    for(int i=0;i<=100000;i++){
        C[i][0]=1; for(int j=1;j<=min(i,K);j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
    }
    sort(b1+1,b1+1+b1[0]); b1[0]=unique(b1+1,b1+1+b1[0])-(b1+1);
    sort(b2+1,b2+1+b2[0]); b2[0]=unique(b2+1,b2+1+b2[0])-(b2+1);
    for(int i=1;i<=n;i++){
        r[ID1(a[i].x)].push_back(ID2(a[i].y));
        cntAll[ID2(a[i].y)]++;
    }
    for(int i=1;i<=b1[0];i++){
        sort(r[i].begin(),r[i].end());
        for(int j=0,lst=0,sz=r[i].size();j<sz;j++){
            (ans+=(Query(r[i][j]-1)-Query(lst))%MOD*C[j][K]%MOD*C[sz-j][K]%MOD)%=MOD;   
            lst=r[i][j];
            Update(lst,-C[cntAll[lst]-cntNow[lst]][K]*C[cntNow[lst]][K]); 
            cntNow[lst]++;
            Update(lst,C[cntAll[lst]-cntNow[lst]][K]*C[cntNow[lst]][K]); 
        }
    }
    printf("%lld\n",(ans+MOD)%MOD);
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值