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;
}
posted @ 2021-10-06 18:00  Paranoid5  阅读(45)  评论(0编辑  收藏  举报