BZOJ1227: [SDOI2009]虔诚的墓主人
树状数组
题解:
我们发现一个格子的答案就是:
C(left,k)∗C(right,k)∗C(up,k)∗C(down,k)
left、right、up、down分别表示这个点的左右上下有几棵树。
坐标范围很大,想到离散化,而且本题无需关心离散后的坐标与原坐标的转化,因为空行和空列没有贡献,因此下面直接用离散化之后的坐标即可。
离散化后n∗m仍然很大,显然无法一个一个的计算,因此想到看看是否有一片点的贡献是一样的,一起计算。
我们注意到一行中两个相邻点之间的C(left,k)∗C(right,k)是一样的,把它提出来:
C(left,k)∗C(right,k)∗∑x=lrC(up[x],k)∗C(down[x],k)
这个∑可以用树状数组维护。具体来说,开一个x坐标的树状数组,一开始所有点都属于
Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
const int N = 200005;
const ll mod = 2147483648LL;
ll c[N][11], ans;
int n,m,w,k;
int tpx[N],tpy[N],xtot[N],ytot[N],downcnt[N],upcnt[N];
struct BIT{
ll c[N]; int sz;
void init(int _sz){ sz=_sz; memset(c,0,sizeof(c)); }
void add(int x,ll d){ while(x<=sz){ c[x]=(c[x]+d)%mod; x+=x&(-x); } }
ll sum(int x){ ll ans=0; while(x>0){ ans=(ans+c[x])%mod; x-=x&(-x); } return ans; }
ll sum(int l,int r){ if(l<=r) return ((sum(r)-sum(l-1))%mod+mod)%mod; else return 0; }
} bit;
struct Point{
int x,y;
} p[N];
bool cmp(const Point &a, const Point &b){ return a.y<b.y || (a.y==b.y && a.x<b.x); }
void init(){
c[0][0]=1;
for(int i=1;i<=w;i++){
c[i][0]=1; int lim=min(k,i);
for(int j=1;j<=lim;j++){
c[i][j] = (c[i-1][j] + c[i-1][j-1]) %mod;
}
}
}
void insert(int x){
bit.add(x,-(c[downcnt[x]][k]*c[upcnt[x]][k])%mod);
downcnt[x]++; upcnt[x]--;
bit.add(x,(c[downcnt[x]][k]*c[upcnt[x]][k])%mod);
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&w);
for(int i=1;i<=w;i++){ scanf("%d%d",&p[i].x,&p[i].y); tpx[i]=p[i].x; tpy[i]=p[i].y; }
scanf("%d",&k);
sort(tpy+1,tpy+1+w); int tpysz=unique(tpy+1,tpy+1+w)-tpy-1;
for(int i=1;i<=w;i++){ p[i].y=lower_bound(tpy+1,tpy+1+tpysz,p[i].y)-tpy; }
sort(tpx+1,tpx+1+w); int tpxsz=unique(tpx+1,tpx+1+w)-tpx-1;
for(int i=1;i<=w;i++){ p[i].x=lower_bound(tpx+1,tpx+1+tpxsz,p[i].x)-tpx; }
init(); bit.init(tpxsz); sort(p+1,p+1+w,cmp);
for(int i=1;i<=w;i++){ xtot[p[i].x]++; ytot[p[i].y]++; upcnt[p[i].x]++; }
// for(int i=1;i<=w;i++){ printf("(%d,%d)\n",p[i].x,p[i].y); }
int line=p[1].y, lcnt=0;
for(int i=1;i<=w;i++){
if(p[i].y>line){
for(int j=i-1;j && p[j].y==line;j--) insert(p[j].x);
line=p[i].y; lcnt=0;
}
if(i-1 && p[i-1].y==p[i].y){
lcnt++;
ans = (ans +
bit.sum(p[i-1].x+1,p[i].x-1)
* c[lcnt][k] %mod
* c[ytot[p[i].y]-lcnt][k] %mod
) %mod;
// D(i); D(ans); printf("%lld %lld %lld ",bit.sum(p[i-1].x+1,p[i].x-1),c[lcnt][k],c[ytot[p[i].y]-lcnt][k]); E;
}
}
printf("%lld\n",ans);
}
本文介绍了解决BZOJ1227题目的一种高效算法,通过离散化处理和树状数组的应用来优化计算过程。该方法利用了特定的数学组合公式,并通过一次计算多个点的贡献来提高效率。
1878

被折叠的 条评论
为什么被折叠?



