BZOJ 1227 虔诚的墓主人

Problem


三倍经验题!惊喜不惊喜?
BZOJ
洛谷
Codevs ←欲知为何WA请戳这个!!!

Solution


我想大家一定都会 O(nm) O ( n m ) 的做法吧,就是暴力扫整个地图??
那只需要做一个简单的离散化就可以把复杂度优化到 O(w2) O ( w 2 )
然而可能你可能会发现你还是只拿了暴力分

设第i个点上方、下方、左方、右方的常青树个数为 ui,di,li,ri u i , d i , l i , r i ,可以知道

ans=i=1w2CkuiCkdiCkliCkri a n s = ∑ i = 1 w 2 C u i k C d i k C l i k C r i k

我们不妨考虑在相同一行的两棵相邻常青树之间的空地(按列考虑也没毛病),它们都有相同的li和ri,且可以方便地求出来。那么不妨提取这两个公因式,那么要求出它们的贡献就只需要求出
CkuiCkdi ∑ C u i k C d i k

那么我们将正在处理的一行的 CkuiCkdi ∑ C u i k C d i k 压进树状数组中,然后算ans的时候,对于区域 [x[i1]+1,x[i]1] [ x [ i − 1 ] + 1 , x [ i ] − 1 ] 直接query(x[i]-1,x[i-1])即可。
为了维护树状数组,我们还需要考虑向下一行的转移。为了方便转移,我们用num数组表示某一列已经扫过的常青树的个数(即在下方的常青树个数),当我们扫过这一棵树的时候,因为懒得推公式,直接暴力算出来现在的贡献,减去原来的贡献修改即可。

Code


#include 
#include 
#include 
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int maxn=100010;
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{
    int x,y;
    bool operator < (const data &t)const
    {
        if(y==t.y) return xreturn yint n,m,w,k,tmp,cnt,ans,x[maxn],y[maxn],h[maxn],l[maxn],t[maxn];
int num[maxn],c[maxn][11];
void add(int pos,int val){for(;pos<=w;pos+=lowbit(pos)) t[pos]+=val;}
int query(int pos)
{
    int res=0;
    for(;pos;pos-=lowbit(pos)) res+=t[pos];
    return res;
}
void input()
{
    read(n);read(m);read(w);
    for(int i=1;i<=w;i++)
      read(p[i].x),read(p[i].y),p[i].x++,p[i].y++,x[i]=p[i].x,y[i]=p[i].y;
    read(k);
    sort(x+1,x+w+1);sort(y+1,y+w+1);
    for(int i=1;i<=w;i++)
    {
        p[i].x=lower_bound(x+1,x+w+1,p[i].x)-x;
        p[i].y=lower_bound(y+1,y+w+1,p[i].y)-y;
        l[p[i].x]++;h[p[i].y]++;
    }
    sort(p+1,p+w+1);
}
void init()
{
    c[0][0]=1;
    for(int i=1;i<=w;i++)
      for(int j=0;j<=10&&j<=i;j++)
        c[i][j]=(j?c[i-1][j-1]:0)+c[i-1][j];
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    input();
    init();
    for(int i=1;i<=w;i++)
    {
        if(i^1&&p[i].y==p[i-1].y)//the same row
        {
            cnt++;
            tmp=query(p[i].x-1)-query(p[i-1].x);
            ans+=tmp*(c[cnt][k]*c[h[p[i].y]-cnt][k]);
        }
        else cnt=0;
        num[p[i].x]++;
        tmp=c[num[p[i].x]][k]*c[l[p[i].x]-num[p[i].x]][k];
        tmp-=c[num[p[i].x]-1][k]*c[l[p[i].x]-num[p[i].x]+1][k];
        add(p[i].x,tmp);
    }
    printf("%d\n",ans&2147483647);
    return 0;
}

你可能感兴趣的:(树状数组,好题集,=====数学=====,BZOJ)