P2154 [SDOI2009]虔诚的墓主人

【题意】链接

【分析】由于题目的N,M极大,所以我们要通过离散W来处理,我们考虑到两个树之间在当前方向上没有区别,所以一起计算

这里就用到了类似扫描线的思想,离散化后,从左到右,从下到上来处理,如果当前点和前一个位置在同一个列上,那么它们之间的所有位置都是上下方案数固定,只需要求在这两个高度区间内的所有左右方案数的和,这里我们采取了线段树来维护。如果两个不在同一列上,我们在这些位置上对其进行线段树的update,考虑每次新加入节点对加入行的贡献即可(准确的说是用新贡献覆盖旧的)

【代码】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int n,m,w,k;
struct segtree
{
    int l,r,sum;
}tr[maxn<<2];
int cnt,p[maxn<<2],C[maxn][12],li[maxn],qu[maxn],dwn[maxn],lft[maxn];
struct corner
{
    int x,y;
}e[maxn<<1];
bool cmp(corner a,corner b)
{
    if(a.x!=b.x) return a.x<b.x;
    return a.y<b.y;
}
#define lson now<<1
#define rson now<<1|1
void update(int now,int L,int R,int v,int val)
{
    if(L==R)
    {
        tr[now].sum=val;
        return;
    }
    int mid=L+R>>1;
    if(v<=mid) update(lson,L,mid,v,val);
    if(v>mid) update(rson,mid+1,R,v,val);
    tr[now].sum=tr[lson].sum+tr[rson].sum;
}
int query(int now,int L,int R,int l,int r)
{
    if(L>R) return 0;
    if(L>=l && R<=r) return tr[now].sum;
    int mid=L+R>>1;
    int res=0;
    if(l<=mid) res+=query(lson,L,mid,l,r);
    if(r>mid) res+=query(rson,mid+1,R,l,r);
    return res;
}
int main()
{
    freopen("bomb.in","r",stdin);
    freopen("bomb.out","w",stdout);
    scanf("%d%d%d",&n,&m,&w);
    int x,y;
    for(int i=1;i<=w;i++)
    {
        scanf("%d%d",&x,&y);
        e[i].x=x; e[i].y=y;
        p[++cnt]=x; p[++cnt]=y;
    }
    sort(p+1,p+cnt+1);
    scanf("%d",&k);
    int cntt=unique(p+1,p+cnt+1)-p-1;
    for(int i=1;i<=w;i++)
    {
        e[i].x=lower_bound(p+1,p+cntt+1,e[i].x)-p;
        e[i].y=lower_bound(p+1,p+cntt+1,e[i].y)-p;
        qu[e[i].x]++; li[e[i].y]++;
    }
    sort(e+1,e+w+1,cmp);
    for(int i=0;i<=w;i++)
        C[i][0]=1;
    for(int i=1;i<=w;i++)
        for(int j=1;j<=min(10,i);j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    int ans=0;
    for(int i=1;i<=w;i++)
    {
        x=e[i].x; y=e[i].y;
        if(x==e[i-1].x)
        {
            dwn[x]++;
            if(dwn[x]>=k && qu[x]-dwn[x]>=k)
            {
                int delta=y-e[i-1].y;
                if(delta>1)
                    ans+=C[dwn[x]][k]*C[qu[x]-dwn[x]][k]*query(1,1,w,e[i-1].y+1,y-1);
            }
        }
        lft[y]++;
        if(lft[y]>=k && li[y]-lft[y]>=k) update(1,1,w,y,C[lft[y]][k]*C[li[y]-lft[y]][k]);
        else update(1,1,w,y,0);
    }
    if(ans<0) ans+=2147483648ll;
    printf("%lld",ans);
    return 0;
}

 

 
posted @ 2021-05-10 22:27  andyc_03  阅读(47)  评论(0)    收藏  举报