【Nowcoder 1103A】复读数组

题目大意:

有一个长为 \(n\times k\) 的数组,它是由长为 \(n\) 的数组 \(A_1,A_2,\cdots,A_n\) 重复 \(k\) 次得到的。
定义这个数组的一个区间的权值为它里面不同的数的个数,现在,你需要求出对于这个数组的每个非空区间的权值之和。

正文:

有一个关键的性质:答案就是每一个数字能做贡献的区间数量之和。那么我们只用枚举最近的两个相同数字,然后用总区间减去两个数字之间的非空子区间个数即可。

代码:

const int N = 1e5 + 10;
const ll mod = 1e9 + 7;
 
inline ll Read()
{
    ll x = 0, f = 1;
    char c = getchar();
    while (c != '-' && (c < '0' || c > '9')) c = getchar();
    if (c == '-') f = -f, c = getchar();
    while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
    return x * f;
}
 
ll n, t;
ll m, a[N], ans, b[N];
 
vector <int> pos[N];
 
ll Cnt(ll l, ll r)
{
    return (r - l + 1) * (r - l + 2) % mod * 500000004ll % mod;
}
 
int main()
{
    n = Read(), m = Read();
    for (int i = 1; i <= n; i++) b[i] = a[i] = Read();
    sort (b + 1, b + 1 + n);
    t = unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= n; i++) pos[lower_bound(b + 1, b + 1 + t, a[i]) - b].push_back(i);
    for (int i = 1; i <= t; i++)
    {
        int len = pos[i].size();
        ll tmp = 0;
        (tmp += (Cnt(1, pos[i][0] - 1) + Cnt(pos[i][len - 1] + 1, n)) % mod) %= mod;
        for (int j = 1; j < len; j++)
            (tmp += Cnt(pos[i][j - 1] + 1, pos[i][j] - 1) * m % mod) %= mod;
        (tmp += Cnt(pos[i][len - 1] + 1, pos[i][0] + n - 1) * (m - 1) % mod) %= mod;
        ans = (ans + Cnt(1, n * m % mod) - tmp) % mod;
    }
    printf ("%lld\n", (ans % mod + mod) % mod);
    return 0;
}
posted @ 2021-09-26 13:41  Jayun  阅读(34)  评论(0编辑  收藏  举报