1. 程式人生 > >[SDOI2009]虔誠的墓主人,洛谷之提高歷練地,其他數學問題

[SDOI2009]虔誠的墓主人,洛谷之提高歷練地,其他數學問題

正題

      這個東西,根據題意我們可以知道當前點的虔誠度為C(up,k)*C(down,k)*C(left,k)*C(right,k);

      這裡的四個英文單詞,分別表示上面的常青樹個數,下面的常青樹個數,左邊的常青樹個數和右邊的。

      我們現在要做的事情就是如何維護這四個值。

      對於30分做法,很明顯就是用四個陣列來儲存,up[],down[],left[],right[]。

      過一遍,遇到一棵常青樹就維護一下這四個值。

      這樣不僅會MLE,而且還會TLE。

      我們提出一種想法,就是說在同一行的兩棵常青樹之間,這個區間的left[]和right[]是不變的。

      如果我們知道這個,再將座標離散化,就可以拿到70分了。

      關鍵是怎麼維護up[],和down[].

線段樹

      沒錯,就是這個,一個區間的up[]和down[]很明顯可以用一個線段樹來進行維護和區間求和。

      我們可以先將整張圖先離散化,按x為第一關鍵字,按y為第二關鍵字排一次序,然後一棵一棵線段樹進行遍歷,如果這棵常青樹和上一棵常青樹在同一行。

      那麼就可以用線段樹求一下s[i-1].y+1到s[i].y-1這個區間的C(up[p],k)*C(down[p],k);

      很明顯當我們遍歷完這棵常青樹,這一行後面的區間都不會和這一列有關,所以可以直接維護線段樹,使

up[s[i].y]++;down[s[i].y]--;再更改維護一下線段樹即可。

程式碼<求C要老老實實用楊輝三角形哦>

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n,m,w;
struct node{int x,y;};
node s[100010];
int k;
int c[100010][11];
struct tree{int x,y,ls,rs,tot;};
tree tr[200010];
int len=0;
int toty[100010];
int totx[100010];
int up[100010];

void bt(int x,int y)
{
    len++;
    int i=len;
    tr[i].x=x;tr[i].y=y;
    tr[i].ls=-1;tr[i].rs=-1;
    tr[i].tot=0;
    if(x==y) return ;
    int mid=(x+y)/2;
    tr[i].ls=len+1;bt(x,mid);
    tr[i].rs=len+1;bt(mid+1,y);
}

void qsort_y(int x,int y)
{
    int i=x,j=y;
    node m,t;
    m=s[(x+y)/2];
    while(i<=j)
    {
        while(s[i].y<m.y) i++;
        while(s[j].y>m.y) j--;
        if(i<=j)
        {
            t=s[i];
            s[i]=s[j];
            s[j]=t;
            i++;j--;
        }
    }
    if(x<j) qsort_y(x,j);
    if(i<y) qsort_y(i,y);
}

void qsort_x(int x,int y)
{
    int i=x,j=y;
    node m,t;
    m=s[(x+y)/2];
    while(i<=j)
    {
        while(s[i].x<m.x || (s[i].x==m.x && s[i].y<m.y)) i++;
        while(s[j].x>m.x || (s[j].x==m.x && s[j].y>m.y)) j--;
        if(i<=j)
        {
            t=s[i];
            s[i]=s[j];
            s[j]=t;
            i++;j--;
        }
    }
    if(x<j) qsort_x(x,j);
    if(i<y) qsort_x(i,y);
}

int get_tot(int p,int x,int y)//求和
{
    if(tr[p].x==x && tr[p].y==y) return tr[p].tot;
    int mid=tr[tr[p].ls].y;
    if(y<=mid) return get_tot(tr[p].ls,x,y);
    else if(x>mid) return get_tot(tr[p].rs,x,y);
    else return get_tot(tr[p].ls,x,mid)+get_tot(tr[p].rs,mid+1,y);
}

void change(int p,int x,int t)
{
    if(tr[p].ls==-1 && tr[p].rs==-1) 
    {
        tr[p].tot=t;
        return;
    }
    int mid=tr[tr[p].ls].y;
    if(x<=mid) change(tr[p].ls,x,t);
    else if(x>mid) change(tr[p].rs,x,t);
    tr[p].tot=tr[tr[p].ls].tot+tr[tr[p].rs].tot;
}

int main()
{
	memset(up,0,sizeof(up));
    scanf("%d %d %d",&n,&m,&w);
    n++;m++;
    for(int i=1;i<=w;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        x++;y++;
        s[i].x=x;s[i].y=y;
    }
    scanf("%d",&k);
    for(int i=0;i<=w;i++)
    	c[i][0]=1;
    for(int i=1;i<=w;i++)
    	for(int j=1;j<=min(i,k);j++)
    		c[i][j]=c[i-1][j]+c[i-1][j-1];
    qsort_y(1,w);
    int t=0;
    int last=-1,now=0;
    for(int i=1;i<=w;i++)
    {
        now=s[i].y;
    	if(s[i].y!=last) s[i].y=++t;
    	else s[i].y=t;
        last=now;
    }
    qsort_x(1,w);
    t=0;
    last=-1;now=0;
    for(int i=1;i<=w;i++)
    {
        now=s[i].x;
        if(s[i].x!=last) s[i].x=++t;
        else s[i].x=t;
    	last=now;
    }
    bt(1,w);
    for(int i=1;i<=w;i++)
        toty[s[i].y]++;
    for(int i=1;i<=w;i++)
        totx[s[i].x]++;
    int tt=0;
    int inall=0;
    for(int i=1;i<=w;i++)
    {
        if(s[i].x!=s[i-1].x) tt=0;
        int now=c[tt][k]*c[totx[s[i].x]-tt][k];
        if(s[i-1].x==s[i].x && s[i].y!=s[i-1].y+1){//當在同一行時才進去更新。
	    now*=get_tot(1,s[i-1].y+1,s[i].y-1);
    	    inall+=now;
    	}
	    change(1,s[i].y,c[up[s[i].y]+1][k]*c[toty[s[i].y]-up[s[i].y]-1][k]);//幫下一行的up[s[i].y]更新。
        up[s[i].y]++;
        tt++;
    }
    printf("%d",inall<0?inall+2147483648ll:inall);
}