1. 程式人生 > >【bzoj 1227】虔誠的墓主人(樹狀陣列)

【bzoj 1227】虔誠的墓主人(樹狀陣列)

傳送門biu~
上下左右沒有樹的墓地必然沒有虔誠度,所以將原圖離散化成一個最大為w×w的圖。
對於每塊墓地,令在它上、下、左、右方向常青樹的數目分別為uplr。則以這個點為中心的十字架的數目是Cuk×Cpk×Clk×Crk
顯然列舉每塊墓地,時間複雜度為O(w2)會TLE。那麼我們考慮在同一行,即x座標相同的兩棵常青樹之間的所有墓地,它們的lr值是相同的。那麼這些墓地的虔誠度之和為Clk×Crk×ΣCuk×ΣCdk。採用樹狀陣列來求和。
首先預處理出每棵樹的uplr值,按行列舉每棵樹。用當前行的樹來修改樹狀陣列中

ΣCuk×ΣCdk的值。每次將兩棵樹之間的貢獻加到答案裡。
Ps:因為模數特殊,可以直接用int的自然溢位,最後取答案的後31位即可。

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
struct data{int x,y,l,r,u,d;}a[100005];
inline bool cmp(data a,data b){return a.y<b.y || a.y==b.y && a.x<b.x;}
int n,m,w,k,ans,len;
int
tree[100005],b[100005],num[100005],c[100005][15]; inline void getC(int k){ for(int i=0;i<=w;++i){ c[i][0]=1; int go=min(i,k); for(int j=1;j<=go;++j) c[i][j]=c[i-1][j]+c[i-1][j-1]; } } inline void add(int x,int val){ for(int i=x;i<=len;i+=lowbit(i)) tree[i]+=val; } inline
int search(int x){ int re=0; for(int i=x;i;i-=lowbit(i)) re+=tree[i]; return re; } int main(){ scanf("%d%d%d",&n,&m,&w); for(int i=1;i<=w;++i) scanf("%d%d",&a[i].x,&a[i].y),b[i]=a[i].x; sort(b+1,b+w+1); len=unique(b+1,b+w+1)-b-1; scanf("%d",&k);getC(k); sort(a+1,a+w+1,cmp); for(int cnt=0,y=-1,i=1;i<=w;++i){ a[i].x=lower_bound(b+1,b+len+1,a[i].x)-b; if(a[i].y!=y) y=a[i].y,cnt=0; a[i].l=cnt++; a[i].d=num[a[i].x]++; } for(int cnt=0,y=-1,i=w;i>=1;--i){ if(a[i].y!=y) y=a[i].y,cnt=0; a[i].r=cnt++; a[i].u=num[a[i].x]-a[i].d-1; } for(int i=1;i<=w;++i){ add(a[i].x,c[a[i].u][k]*c[a[i].d+1][k]-(search(a[i].x)-search(a[i].x-1))); if(i>1 && a[i].y==a[i-1].y) ans+=c[a[i-1].l+1][k]*c[a[i].r+1][k]*(search(a[i].x-1)-search(a[i-1].x)); } printf("%d",ans&(~0u>>1)); return 0; }