bzoj1227 [SDOI2009]虔诚的墓主人

传送门

一上来先想到n2 做法,即枚举每一个墓地,排列组合计算虔诚度,但这个时间复杂度是不现实的,所以要进行转化,改为用枚举常青树来计算相邻常青树间墓地的虔诚度之和,由于两个相邻的常青树之间的墓地左右的常青树数目都是相同的,所以问题转化为维护相邻常青树之间所有墓地上下常青树数目形成的十字架的个数。又由于枚举到的两个常青树之间的墓地上下常青树数目一定可以在之前维护出来(枚举到一棵常青树的时候在记录数组中将这一列属于上半部分的个数减1,属于下半部分的个数加1,然后用新的组合数乘积更新区间和,可用树状数组维护),而每棵常青树左右的树的数目又可以来回扫两遍计算,所以题目就解决了。
我的语文太差了,如果看不懂就看代码吧。。
ps:吐槽数据弱,计算杨辉三角的时候只枚举列数或行数,只会错1到2个点,一定要枚举到行数和列数的最大值!

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lowbit(x) x&(-x)
const int N=1e5+5;
struct node
{
    int x,y;
    inline bool operator <(const node other)const
    {
        if(y==other.y) return x<other.x;
        return y<other.y;
    }
}p[N];
int L[N],R[N],a[N];
int u[N],d[N];
int c[N][15];
int tmp1[N],tmp2[N],tmp;
int n,m,w,k,tot1,tot2,ans,l,r;
inline void add(int a[],int pos,int num)
{
    for(int i=pos;i<=tot1;i+=lowbit(i))
      a[i]+=num;
}
inline int ask(int a[],int pos)
{
    int ans=0;
    for(int i=pos;i;i-=lowbit(i))
      ans+=a[i];
    return ans;
}
int main()
{
    scanf("%d%d%d",&n,&m,&w);
    for(int i=1;i<=w;i++)
      scanf("%d%d",&p[i].x,&p[i].y),tmp1[i]=p[i].x,tmp2[i]=p[i].y;
    scanf("%d",&k);
    sort(p+1,p+w+1);
    sort(tmp1+1,tmp1+w+1);
    sort(tmp2+1,tmp2+w+1);
    tot1=unique(tmp1+1,tmp1+w+1)-tmp1-1;
    tot2=unique(tmp2+1,tmp2+w+1)-tmp2-1;
    for(int i=1;i<=w;i++)
      p[i].x=lower_bound(tmp1+1,tmp1+tot1+1,p[i].x)-tmp1,
      p[i].y=lower_bound(tmp2+1,tmp2+tot2+1,p[i].y)-tmp2;
    c[0][0]=1;
    for(int i=1;i<=max(tot1,tot2);i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i&&j<=k;j++)
          c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
    for(int i=1;i<=w;i++)
    {
        if(p[i].y==p[i-1].y) tmp++;
        else tmp=0;
        L[i]=tmp;
    }
    tmp=0;
    for(int i=w;i;i--)
    {
        if(p[i].y==p[i+1].y) tmp++;
        else tmp=0;
        R[i]=tmp;
    }
    for(int i=1;i<=w;i++)
      u[p[i].x]++;
    for(int i=1;i<=tot2;i++)
    {
        l=++r;
        while(r<=w&&p[r].y==i) r++;
        r--;
        add(a,p[l].x,-c[d[p[l].x]][k]*c[u[p[l].x]][k]);
        d[p[l].x]++,u[p[l].x]--;
        add(a,p[l].x,c[d[p[l].x]][k]*c[u[p[l].x]][k]);
        for(int j=l+1;j<=r;j++)
        {
            add(a,p[j].x,-c[d[p[j].x]][k]*c[u[p[j].x]][k]);
            d[p[j].x]++,u[p[j].x]--;
            add(a,p[j].x,c[d[p[j].x]][k]*c[u[p[j].x]][k]);
            ans+=c[L[j]][k]*c[R[j-1]][k]*(ask(a,p[j].x-1)-ask(a,p[j-1].x));
        }
    }
    printf("%d",ans&2147483647);
    return 0;
}

反思:思维的转化是很重要的,有时候数据范围太大,可以思考用较小的量来代替计算,还有一定要注意枚举的范围这个细节,经常会出错!

发布了137 篇原创文章 · 获赞 6 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章