Solution [SDOI2009]虔诚的墓主人
题目大意:给定一个有((n + 1) imes(m + 1))个点的矩阵,点分黑白,记每个点上下左右的黑点数为(U),(D),(L),(R),定义一个点的贡献为(C_{U}^{k} imes C_{D}^{k} imes C_{L}^{k} imes C_{R}^{k}),求所有白点贡献之和,答案对(2^{32})取模
组合数学,树状数组(线段树)
分析:首先(n)和(m)范围巨大,我们不可能(O(nm))枚举,这样无法承受,我们考虑把不可能对答案有贡献的点给剔除掉
首先看计算答案的式子,如果其中有一项为(0)那么整个式子为(0),这样那些没有黑点的行和列就可以不用枚举,复杂度仍为(O(nm))
但是这样做可以给我们以启示,我们按列来计算,枚举所有有黑点的列。如果我们能快速算出每一列的贡献就可以解决问题
对答案有贡献的只有可能是白点,而每一列的黑点都将整列分为了很多段,观察式子发现,这些段里面的点的贡献的前两项是不变的,我们可以考虑用乘法分配律提出来,我们只需要对于每一段,快速算出后两项乘积的和即可
由于(k)非常小,我们可以预处理出组合数,这样每一列就是单点修改,区间求和问题,可以用树状数组来维护,由于点分布的较为稀疏,(n)和(m)范围又较大,采用离散化处理,然后用树状数组维护
每个点只会在询问和每一列询问完修改树状数组时对时间复杂度产生贡献,因此复杂度在(O(WlogW))级别
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 100,maxk = 16;
const ll mod = 2147483648ll;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
struct Point{
int x,y;
}point[maxn];
int n,m,siz,w,k,tmp[maxn],sum[maxn],now[maxn];
inline ll mul(ll a,ll b){return (a * b) % mod;}
inline int getid(int x){return lower_bound(tmp + 1,tmp + 1 + siz,x) - tmp;}
inline void discretize(){
sort(tmp + 1,tmp + 1 + siz);
siz = unique(tmp + 1,tmp + 1 + siz) - tmp - 1;
for(int i = 1;i <= w;i++)
point[i].x = getid(point[i].x),point[i].y = getid(point[i].y);
n = m = 0;
for(int i = 1;i <= w;i++)
n = max(n,point[i].x),m = max(m,point[i].y);
}
namespace BIT{
ll f[maxn];
inline int lowbit(int x){return x & (-x);}
inline void add(int pos,ll x){
while(pos <= m){
f[pos] += x;
pos += lowbit(pos);
}
}
inline ll query(int pos){
ll res = 0;
while(pos){
res += f[pos];
pos -= lowbit(pos);
}
return res;
}
inline ll query(int a,int b){return a > b ? 0 : query(b) - query(a - 1);}
inline void modify(int pos,ll x){
add(pos,-query(pos,pos));
add(pos,x);
}
}
ll C[maxn][maxk],ans;
inline void init(){
C[0][0] = 1;
for(int i = 1;i < maxn;i++){
C[i][0] = 1;
for(int j = 1;j < maxk;j++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
vector<int> vec[maxn];
int main(){
init();
n = read(),m = read();
w = read();
for(int i = 1;i <= w;i++){
tmp[++siz] = point[i].x = read();
tmp[++siz] = point[i].y = read();
}
discretize();
k = read();
for(int i = 1;i <= w;i++)
vec[point[i].x].push_back(point[i].y),sum[point[i].y]++;
for(int i = 1;i <= n;i++)
sort(vec[i].begin(),vec[i].end());
for(int i = 1;i <= n;i++){
for(int j = 0;vec[i].size() >= 2 && j < vec[i].size() - 1;j++){
ans += mul(mul(C[j + 1][k],C[vec[i].size() - j - 1][k]),BIT::query(vec[i][j] + 1,vec[i][j + 1] - 1)),ans %= mod;
}
for(auto x : vec[i]){
now[x]++;
BIT::modify(x,mul(C[now[x]][k],C[sum[x] - now[x]][k]));
}
}
printf("%lld
",ans);
return 0;
}