• P2154 [SDOI2009]虔诚的墓主人


    P2154 [SDOI2009]虔诚的墓主人


    树状数组扫描线题

    用树状数组维护一些互不相干的组合数的区间和

    这一道题,如果从N,M入手。确实是不好做的

    所以要考虑从W入手。

    很容易发现,对于一块墓地,我们要求(C_{ ext{上方树的个数}}^kcdot C_{ ext{下方树的个数}}^kcdot C_{ ext{左方树的个数}}^kcdot C_{ ext{右方树的个数}}^k)

    然后又可以发现一条很明显的性质,在某一行,两颗树之间,(C_{ ext{左方树的个数}}^kcdot C_{ ext{右方树的个数}}^k)是不变的。列也是如此。

    某一列两棵树之间墓地虔诚值的部分组合数的乘积可以在O(1)内处理出来,剩下的便是算左右两边树的组合数乘积。

    其实,在算这个的时候。很明显,对于在这之中的某一块墓地,我们不会动左右两边树,所以的两边树的组合数乘积也是不变的。我们所要做的便是将他们(连续的墓地的组合数)加起来。

    我们需要优化的是这个过程,也就是区间加。然后就可以使用树状数组维护。

    那这些组合数光讨论的不变,那什么时候变动呢?

    首先对于上下两边树的组合数,肯定是在我们更换所以来的树的时候变换的。

    那么左右两边树的呢?在我们扫描完一颗树的时候,这棵树就不会被使用了,就可以更改这个值了。

    // luogu-judger-enable-o2
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    using std::sort;
    const int maxn=101000;
    const long long mode=2147483648LL;
    struct node
    {
        int x,y;
        int X,Y;
        bool operator < (const node &a)const
        {
            if(x!=a.x)  return x<a.x;
            return y<a.y;
        }
    };
    node T[maxn];
    long long last[maxn],Layt[maxn];
    long long C[maxn][11];
    long long ans;
    int totX[maxn],totY[maxn];
    int baseX[maxn],lenX;
    int baseY[maxn],lenY;
    int base[maxn];
    int N,M,w,k;
    int length;
    void insert(long long val,int pos)//Begin:树状数组
    {
        while(pos<=length)
        {
            base[pos]=(base[pos]+val)%mode;
            pos+=(pos&(-pos));
        }
        return ;
    }
    long long sum(int pos)
    {
        if(pos==0)  return 0;
        long long res=0;
        while(pos)
        {
            res=(res+base[pos])%mode;
            pos-=(pos&(-pos));
        }
        return res;
    }
    long long query(int l,int r)
    {
        return sum(r)-sum(l-1);
    }//End:树状数组
    void unique(int *A,int &Len)//去重
    {
        sort(A+1,A+Len+1);
        int len=0,now=0x7fffffff;
        for(int i=1;i<=Len;i++)
            if(now!=A[i])
            {
                now=A[i];
                A[++len]=A[i];
            }
        Len=len;
        return;
    }
    void init(int x)//组合数
    {
        C[0][0]=1;
        for(int i=1;i<=x;i++)
            for(int j=0;j<=10;j++)
                C[i][j]=(C[i-1][j]+(j?C[i-1][j-1]:0))%mode;
        return ;
    }
    int check(int *A,int len,long long val)//离散化查询
    {
        int l=1,r=len;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(A[mid]>=val) r=mid;
            else    l=mid+1;
        }
        return l;
    }
    void account(int n)//统计离散化后每一列,每一行共有多少个树
    {
        int nowX,nowY;
        for(int i=1;i<=n;i++)
        {
            nowX=check(baseX,lenX,T[i].x);
            nowY=check(baseY,lenY,T[i].y);
            totY[nowY]++;
            totX[nowX]++;
            T[i].X=nowX;T[i].Y=nowY;
        }
        length=lenY;
    }
    void add(int Y,int K)//将一颗树填进树状数组
    {
        if(last[Y])//last为上一次填进去的数字
        {
            insert(-last[Y],Y);//删除
        	last[Y]=0;//置零
    	}
        Layt[Y]++;//这一行中,已经扫过的的个数
        if(Layt[Y]>=K&&totY[Y]-Layt[Y]>=K)//满足计算的条件
        {
            insert((C[Layt[Y]][K]*C[totY[Y]-Layt[Y]][K])%mode,Y);//计算出新的组合数来,填进树状数组
            last[Y]=(C[Layt[Y]][K]*C[totY[Y]-Layt[Y]][K])%mode;//更新last
        }
    }
    long long clac(int l,int r)
    {
    	if(l>r)	return 0;//防止爆负数
        return query(l,r);//计算
    }
    void work(int n,int K)//解决问题
    {
        int nowX=-1,now;
        for(int i=1;i<=n;i++)
        {
            if(nowX!=T[i].X)//与上一棵树不在同一列中
            {
                now=0;//计数器归零
                nowX=T[i].X;
            }
            now++;
            if(totX[nowX]-now>=k&&now>=K&&T[i+1].X==nowX)//满足同一列中的计算标准
                ans=(ans+(C[now][k]*(clac(T[i].Y+1,T[i+1].Y-1)*C[totX[nowX]-now][k])%mode)%mode)%mode;//计算
            add(T[i].Y,K);//将这颗树填进树状数组
        }
    }
    int main()
    {
        scanf("%d%d%d",&N,&M,&w);
        for(int i=1;i<=w;i++)
        {
            scanf("%d%d",&T[i].x,&T[i].y);
            baseX[i]=T[i].x;
            baseY[i]=T[i].y;
        }
        scanf("%d",&k);
        unique(baseX,lenX=w);//离散化
        unique(baseY,lenY=w);
        sort(T+1,T+1+w);//按照x为第一关键字,y为第二关键字排序
        init(w);//预处理组合数
        account(w);//统计同一行、列中的树的个数
        work(w,k);//进行计算
        printf("%lld",(ans+mode)%mode);
    }
    
    
  • 相关阅读:
    第四次作业——个人作业——软件案例分析
    作业五——团队项目——需求规格说明书
    团队项目——团队展示
    作业三——结对编程
    作业二——结对项目之需求分析与原型模型设计
    leetcode 212 单词搜索II
    leetcode 130. 被围绕的区域
    leetcode 695 Max Area of Island 岛的最大面积
    【《算法》学习笔记】一:
    leetcode 191 位1的个数
  • 原文地址:https://www.cnblogs.com/Lance1ot/p/9818494.html
Copyright © 2020-2023  润新知