[SDOI2009]虔誠的墓主人,洛谷之提高歷練地,其他數學問題
阿新 • • 發佈:2019-02-12
正題
這個東西,根據題意我們可以知道當前點的虔誠度為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); }