P6692 出生点
P6692 出生点
不难想到,答案就是选择所有的,减去至少选择一次的,加上选择两次的。
\(A_i\)表示第\(i\)个屏蔽点被选择了。
选择两个屏蔽点的:
因为每一个点横坐标和纵坐标之间没有相互影响的关系,可以分别看待。
以横坐标为例,我们可以把横坐标排序,那么后面的一定比前面的大,这样就消除绝对值的影响了。
选择第\(i\)个屏蔽点来看看:
\(ans = (x_i - x_1)+(x_i-x_2)+(x_i-x_3)+...+(x_i-x_{i-1})\),求和就可以得到\(ans = (i-1)x_i-\sum _{i=1}^{n-1}x_j\),后者就是前缀和,再对每一个横坐标求和,这样就是\(O(n)\)的了。
纵坐标同理。
选择一个屏蔽点的:
这里说的是至少选择一个,不是只选择一个。
一样对每一个屏蔽点分开考虑横纵坐标,并且以横坐标为例,分开后发现,每一列对于该点的贡献值都是相同的。不妨只考虑当前列,最后答案乘上\(m\)即可。
假设坐标为\((i,j)\),\(ans = m*\frac {((i-1)i+(n-i)(n-i+1))}{2}\),纵坐标调换\(m,n\)即可。
全部都选的:
和上面一样,直接给公式吧。
\(ans = \frac{mn(mn-1)(m+n)}{6}\)
code
struct P{
ll x,y;
}pt[N];
ll f_pow(ll a,ll b){
ll res = 1;
while(b){
if(b&1) res = res*a%mod;
a = a*a%mod;
b>>=1;
}
return res;
}
ll n,m,k;
ll get(P p){
ll a,b;
a = (p.x*(p.x-1)%mod+(n-p.x)*(n-p.x+1)%mod)%mod;
b = (p.y*(p.y-1)%mod+(m-p.y)*(m-p.y+1)%mod)%mod;
ll tmp = (a*m%mod+b*n%mod)%mod;
return tmp*f_pow(2,mod-2)%mod;
}
ll mx[N],my[N],sumx[N],sumy[N];
int main() {
cin>>n>>m>>k;
for(ll i = 1;i <= k;i++) {
cin>>pt[i].x>>pt[i].y;
mx[i] = pt[i].x,my[i] = pt[i].y;
}
sort(mx+1,mx+1+k);sort(my+1,my+1+k);
sumx[0] = sumy[0] = 0;
ll dou = 0;
ll tmp1 = m*n%mod,tmp2 = (m+n)%mod;
ll tot = tmp1*tmp2%mod*(tmp1-1+mod)%mod*f_pow(6,mod-2)%mod;
for(ll i = 1;i <= k;i++){
sumx[i] = (sumx[i-1] + mx[i])%mod;
sumy[i] = (sumy[i-1] + my[i])%mod;
ll dx = (i-1)*mx[i]-sumx[i-1],dy = (i-1)*my[i]-sumy[i-1];
dou = (dou + (dx%mod + dy%mod)%mod)%mod;
}
ll tmp = 0;
for(ll i = 1;i <= k;i++){
tmp = (tmp+get(pt[i]))%mod;
}
ll vtot = 0;
ll ans = ((tot+dou)%mod-tmp+mod)%mod;
cout<<ans<<endl;
return 0;
}