【离散化】循环数组

链接

YbtOJ冲刺NOIPB组T2

题目描述

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

思路

一个数a出现的次数 = 总区间个数 - 不出现a的区间个数
那么就可以考虑求不出现a的区间个数
两个a之间的区间不包含a,所以就求这些区间就好了

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#define ll long long

using namespace std;

vector<int> g[100005];
ll n, m, res, k, inv;
const ll mo = 1e9 + 7;
ll ans, v[100005], p[100005];

ll ksm(ll a, ll b, ll p)
{
    
    
	ll re = 1;
	while(b)
	{
    
    
		if(b & 1) re = re * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return re % p;
}

ll read() 
{
    
    
	ll x = 0, flag = 1; char ch = getchar();
	while (ch < '0' || ch > '9') {
    
    if (ch == '-') flag = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9'){
    
    x = x * 10 + ch - '0'; ch = getchar();}
	return x * flag;
}

int work(int len)
{
    
    
	return 1ll * (len + 1) % mo * (len + 2) % mo * inv % mo;
}

int main()
{
    
    
	freopen("loop.in", "r", stdin);
	freopen("loop.out", "w", stdout);
	n = read(); k = read();
	for(int i = 1; i <= n; ++i)
		v[i] = read(), p[i] = v[i];
    sort(p + 1, p + n + 1);
    int tot = unique(p + 1, p + n + 1) - p - 1;
    for (int j = 1; j <= n; ++j) 
		v[j] = lower_bound(p + 1, p + tot + 1, v[j]) - p,
		g[v[j]].push_back(j);
	inv = ksm(2, mo - 2, mo);
	for(int i = 1; i <= tot; ++i)
	{
    
    
		int siz = g[i].size();
		res = 0;
		for(int j = 1; j < siz; ++j)
		{
    
    
			int x = g[i][j - 1] + 1, y = g[i][j] - 1;
			res = (res + 1ll * work(y - x) % mo * k % mo) % mo;
		} 
		int x = g[i][siz - 1], y = g[i][0];
		res = (1ll * res + work(y - 2) % mo) % mo;
		res = (1ll * res + work(n - x - 1) % mo) % mo;
		res = (1ll * res + 1ll * work(y + n - 1 - (x + 1)) % mo * (k - 1) % mo) % mo;
		int Ans = ((work((1ll * n * k - 1) % mo) % mo - res) % mo + mo) % mo;
		ans = (Ans + ans) % mo;
	}
	printf("%lld", ans);
	return 0;
}

Guess you like

Origin blog.csdn.net/LTH060226/article/details/120477802