1. 程式人生 > 其它 >C.Weird Sum(點貢獻)

C.Weird Sum(點貢獻)

C.Weird Sum

Tag

點貢獻

題目來源

Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)

題目大意

給定一個\(n \times m\)的表格,表格的元素代表著顏色的種類,求問所有相同種類的顏色的距離和,定義兩個表格之間的距離為它們的橫座標和縱座標的差之和,例如(1,2)到(3,3)的距離為3

解題思路

  • 我們先考慮某種顏色,比如1。1對答案的貢獻就是任意的兩個1之間的橫座標的差值的和對答案的貢獻 加上 任意的兩個1之間的縱座標的差值的和對於答案的貢獻。更加詳細一點,假設第\(i\)
    個1的座標為\((x_i, y_i)\),那麼1對答案的貢獻就是
\[\sum_{i}\sum_{j}{\left|x_i-x_j\right|} + \sum_{i}\sum_{j}{\left|y_i-y_j\right|} \]
  • 現在我們單獨考慮\(x\)座標下的答案,由於上述式子的複雜度是\(O(n^2)\)的,因此我們需要考慮每個點對於答案的貢獻值
  • 考慮1的\(x\)軸座標中的相鄰兩個點的距離對於答案的貢獻,即這段距離會有哪幾對點的連線經過,那麼很顯然便是這段距離的左邊的點的個數乘上右邊的點的個數,即
\[\sum_{i}\sum_{j}{\left|x_i-x_j\right|} = \sum_{i=2}^n{(x_i-x_{i-1})\times (i-1)\times(n-i+1)} \]

n表示1的個數

AC程式碼

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0)
#define LL long long
#define maxn (int)(1e6 + 10)
#define FFF freopen("out", "w", stdout);

int n , m;
vector<int> mp[maxn];
vector<int> rr[maxn], cc[maxn];

int main ()
{
    IOS;
    cin >> n >> m ;
    for ( int i = 1 ; i <= n ; i++ ) 
    {
        mp[i].push_back(0);
        for ( int j = 1 ; j <= m ; j++ )
        {
            int x ; cin >> x;
            rr[x].push_back(i);
            mp[i].push_back(x);
        }
    }
    for ( int j = 1 ; j <= m ; j++ )
    {
        for ( int i = 1 ; i <= n ; i++ )
        {
            int x = mp[i][j];
            cc[x].push_back(j);
        }
    }
    LL ans = 0;
    for ( int i = 1 ; i <= 100000 ; i++ )
    {
        if ( rr[i].size() > 1 )
        {
            for ( int j = 1 ; j < rr[i].size() ; j++ )
            {
                LL len = rr[i][j] - rr[i][j-1];
                ans += 1ll * (rr[i].size() - j ) * len * j;
            }
        }
        if ( cc[i].size() > 1 )
        {
            for ( int j = 1 ; j < cc[i].size() ; j++ )
            {
                LL len = cc[i][j] - cc[i][j-1];
                ans += 1ll * (cc[i].size() - j) * len * j;
            }
        }
    }
    cout << ans << endl;

}