1. 程式人生 > >2018.10.25 bzo1227: [SDOI2009]虔誠的墓主人(組合數學+掃描線+bit)

2018.10.25 bzo1227: [SDOI2009]虔誠的墓主人(組合數學+掃描線+bit)

傳送門
有點難調啊。其實是我自己sb了
不過交上去1A1A還是平衡了一下心態。
所以這道題怎麼做呢?
我們考慮對於一個點(x,y)(x,y)如果這個點成為中心,正左/右/上/下分別有l/r/u/d/l/r/u/d/棵樹,那麼對於這個點Ans=(lk)(rk)(uk)(dk)Ans=\binom {l} {k}*\binom {r} {k}*\binom {u} {k}*\binom {d} {k}
發現離散化之後直接列舉是O(n2)O(n^2)的。
於是我們用掃描線做。
yy為第一關鍵字,xx為第二關鍵字排個序。
然後用b

itbit維護上下兩側的總方案數,左右的直接在掃的時候統計。
這樣邊做邊更新答案就行了。
程式碼:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const long long mod=1ll
<<31; const int N=1e5+5; int n,k,vx[N],vy[N],sigx,sigy,bit[N],C[N][15],L[N],R[N],U[N],D[N],ans=0; struct Node{int x,y;}p[N]; inline int lowbit(int x){return x&-x;} inline void update(int x,int v){for(int i=x;i<=sigx;i+=lowbit(i))bit[i]+=v;} inline int query(int x){int ret=0;for(int i=x;i;
i-=lowbit(i))ret+=bit[i];return ret;} inline bool cmp(const Node&a,const Node&b){return a.y==b.y?a.x<b.x:a.y<b.y;} int main(){ n=read(),n=read(),n=read(); for(int i=1;i<=n;++i)p[i].x=vx[i]=read(),p[i].y=vy[i]=read(); k=read(),C[1][1]=C[1][0]=1; for(int i=2;i<=n;++i){ C[i][0]=1; for(int j=1;j<=k;++j)C[i][j]=C[i-1][j-1]+C[i-1][j]; } sort(vx+1,vx+n+1),sigx=unique(vx+1,vx+n+1)-vx-1,sort(vy+1,vy+n+1),sigy=unique(vy+1,vy+n+1)-vy-1; for(int i=1;i<=n;++i)p[i].x=lower_bound(vx+1,vx+sigx+1,p[i].x)-vx,p[i].y=lower_bound(vy+1,vy+sigy+1,p[i].y)-vy,++U[p[i].x],++R[p[i].y]; sort(p+1,p+n+1,cmp); for(int i=1;i<=n;++i){ if(p[i-1].y^p[i].y)goto UPD; ans+=C[R[p[i].y]][k]*C[L[p[i].y]][k]*(query(p[i].x-1)-query(p[i-1].x)); UPD:++L[p[i].y],--R[p[i].y],update(p[i].x,-C[U[p[i].x]][k]*C[D[p[i].x]][k]),--U[p[i].x],++D[p[i].x],update(p[i].x,C[U[p[i].x]][k]*C[D[p[i].x]][k]); } cout<<1ll*(ans+mod)%mod; return 0; }