1. 程式人生 > >BZOJ-1227 虔誠的墓主人 樹狀陣列+離散化+組合數學

BZOJ-1227 虔誠的墓主人 樹狀陣列+離散化+組合數學

1227: [SDOI2009]虔誠的墓主人
Time Limit: 5 Sec Memory Limit: 259 MB
Submit: 914 Solved: 431
[Submit][Status][Discuss]

Description
小W 是一片新造公墓的管理人。公墓可以看成一塊N×M 的矩形,矩形的每個格點,要麼種著一棵常青樹,要麼是一塊還沒有歸屬的墓地。當地的居民都是非常虔誠的基督徒,他們願意提前為自己找一塊合適墓地。為了體現自己對主的真誠,他們希望自己的墓地擁有著較高的虔誠度。一塊墓地的虔誠度是指以這塊墓地為中心的十字架的數目。一個十字架可以看成中間是墓地,墓地的正上、正下、正左、正右都有恰好k 棵常青樹。小W 希望知道他所管理的這片公墓中所有墓地的虔誠度總和是多少

Input
第一行包含兩個用空格分隔的正整數N 和M,表示公墓的寬和長,因此這個矩形公墓共有(N+1) ×(M+1)個格點,左下角的座標為(0, 0),右上角的座標為(N, M)。第二行包含一個正整數W,表示公墓中常青樹的個數。第三行起共W 行,每行包含兩個用空格分隔的非負整數xi和yi,表示一棵常青樹的座標。輸入保證沒有兩棵常青樹擁有相同的座標。最後一行包含一個正整數k,意義如題目所示。

Output
包含一個非負整數,表示這片公墓中所有墓地的虔誠度總和。為了方便起見,答案對2,147,483,648 取模。

Sample Input
5 6
13
0 2
0 3
1 2
1 3
2 0
2 1
2 4
2 5
2 6
3 2
3 3
4 3
5 2
2

Sample Output
6

HINT
圖中,以墓地(2, 2)和(2, 3)為中心的十字架各有3個,即它們的虔誠度均為3。其他墓地的虔誠度為0。 對於30%的資料,滿足1 ≤ N, M ≤ 1,000。對於60%的資料,滿足1 ≤ N, M ≤ 1,000,000。對於100%的資料,滿足1 ≤ N, M ≤ 1,000,000,000,0 ≤ xi ≤ N,0 ≤ yi ≤ M,1 ≤ W ≤ 100,000, 1 ≤ k ≤ 10。存在50%的資料,滿足1 ≤ k ≤ 2。存在25%的資料,滿足1 ≤ W ≤ 10000。

Source

題解: 一看資料範圍,肯定要離散化,熟練的打上,然後開始做。 這裡要用到組合來進行求值,大體上的思路是: 如果a,b在同一行,則ans+=c(l[a]+1(包括a),k)*c(r[b]+1,k)再分別乘上ab間的每一個點的c(u[i],k)*c(d[i],k)
l[a],r[a],u[a],d[a]表示一個點上下左右的點數,可以預處理,也可以邊做邊記錄
需要優化時間複雜度,於是要用樹狀陣列維護a到b所有點的c(u[i],k)*c(d[i],k)之和
從左往右處理某行的某一個點時,要將樹狀陣列中該點橫座標位置上的數進行修改
修改的值為就是現在的c(u[i],k)*c[d[i],k]減去原來的,也就是c(u[i],k)*c[d[i],k]-c(u[i]+1,k)*c[d[i]-1,k]

後來發現自己的離散似乎有些不適合,只能1AC19WA,於是改成黃學長的方式才能過

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxw 100010
#define p 2147483648LL
int n,m,w,k,l;
struct data
{
    int x,y;
    bool operator < (const data & A) const
        {
            if (y==A.y) return x<A.x;
            return y<A.y;
        }
}tr[maxw];
long long tree[maxw*2],C[maxw*2][15],ans;
int ls[maxw*2],cnt,num,now[maxw*2];
int xx[maxw*2],yy[maxw*2];

int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int dat)
{
    for (int i=x; i<=w*2; i+=lowbit(i)) tree[i]=(tree[i]+dat)%p;
}
long long query(int x)
{
    long long re=0;
    for (int i=x; i>0; i-=lowbit(i)) re=(re+tree[i])%p;
    return re;
}

int getloc(int dat)
{
    int l=1,r=cnt;
    while (l<=r)
        {
            int mid=(l+r)>>1;
            if (ls[mid]<dat) l=mid+1;
            else if (ls[mid]>dat) r=mid-1;
            else return mid;            
        }
}

void getC()
{
    C[0][0]=1;
    for (int i=1; i<=w; i++)
        {
            C[i][0]=1;
            for (int j=1; j<=min(k,i); j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
        }
}

int main()
{
    n=read(),m=read();
    w=read();
    for (int i=1; i<=w; i++) ls[++cnt]=tr[i].x=read(),ls[++cnt]=tr[i].y=read();
    k=read();
    sort(ls+1,ls+cnt+1);
    //for (int i=2; i<=cnt; i++) if (ls[i]!=ls[i-1]) ls[++num]=ls[i];
    //for (int i=1; i<=w; i++) tr[i].x=getloc(tr[i].x),tr[i].y=getloc(tr[i].y);
    for (int i=1; i<=w; i++) xx[getloc(tr[i].x)]++,yy[getloc(tr[i].y)]++;
    getC(); sort(tr+1,tr+w+1);
    //for (int i=1; i<=w; i++)
        //printf("%d %d\n",getloc(tr[i].x),getloc(tr[i].y));
    for(int i=1;i<=w;i++)
        {
            if(i>1 && tr[i].y==tr[i-1].y)
                l++,ans+=(query(getloc(tr[i].x)-1)-query(getloc(tr[i-1].x)))*(C[l][k]*C[yy[getloc(tr[i].y)]-l][k]),ans%=p;
            else l=0;
            int loc=getloc(tr[i].x); now[loc]++;
            int delta=(C[now[loc]][k]*C[xx[loc]-now[loc]][k]-C[now[loc]-1][k]*C[xx[loc]-now[loc]+1][k])%p;
            add(loc,delta);
        }
    if (ans<0) ans+=p; 
    printf("%lld\n",ans);
    return 0;
}