B. 循环数组

题目描述

有一个长为 的数组,它是由长为 的数组 重复 次得到的。

定义这个数组的一个区间的权值为它里面不同的数的个数,现在,你需要求出对于这个数组的每个非空区间的权值之和。

答案对 取模。

输入格式

从 loop.in 中读入数据。

第一行两个整数 和 。

接下来一行 个整数,第 个整数为 。

输出格式

输出答案到 loop.out 中。

输出一个整数,表示答案。

样例

样例输入

2 2
1 2

样例输出

16

数据范围与提示
在这里插入图片描述

思路

对于本题,可以知道一个数字对答案的贡献是包含这个数字的区间的个数

这个问题比较难求,所以将它转换为总区间个数 – 不包含这个数的区间个数

剩下的看标 ~ _ ~

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <queue>
#include <cmath>
#define re register
#define LL long long
#define INF 100000009
using namespace std;

const LL N=1e6+100,M=2020,mod=1e9+7;
vector < int > q[N];
struct sb { LL wz,ans; } b[N];
LL n,m,gs,ans,sb1,sb2,maxn,a[N],c[N],k[N],f[N],last[N];
bool cmp_1 ( sb a,sb b ) { return a.ans<b.ans; }
bool cmp_2 ( sb a,sb b ) { return a.wz<b.wz; }
void lsh ( )                 // 离散化
{
	LL cnm=0; 
	for ( LL i=1;i<=n;i++ ) b[i].ans=a[i],b[i].wz=i;
	sort ( b+1,b+1+n,cmp_1 );
	for ( LL i=1;i<=n;i++ )
	{
		if ( b[i].ans==b[i-1].ans ) c[i]=cnm;
		else c[i]=++cnm;
	}
	for ( LL i=1;i<=n;i++ ) b[i].ans=c[i];
	sort ( b+1,b+1+n,cmp_2 );
	for ( LL i=1;i<=n;i++ ) a[i]=b[i].ans;
	gs=cnm;
}
LL change ( LL n ) { return ( n+1 )*n/2%mod; }      // 等差数列求区间个数

int main ( )
{
//	freopen ( "loop.in","r",stdin );
//	freopen ( "loop.out","w",stdout );
	scanf ( "%lld %lld",&n,&m );
	for ( LL i=1;i<=n;i++ ) scanf ( "%lld",&a[i] );
	lsh ( );
	for ( LL i=1;i<=n;i++ ) q[a[i]].push_back ( i );         // 看每个数在数组中的位置
	for ( LL i=1;i<=gs;i++ )
	{
		LL len=q[i].size ( );
		for ( int j=1;j<len;j++ ) ans=( ans+change ( q[i][j]-q[i][j-1]-1 )*m%mod )%mod;
		// 求在 1 ~ n 中每两个相同的数之间区间的个数,在对于每个数相应 * k
		ans=( ans+change ( q[i][0]-1 )+change ( n-q[i][len-1] )+change ( q[i][0]-1+n-q[i][len-1] )*( m-1 )%mod )%mod;
		// 求在 1 ~ n 中这个数第一次出现的位置之前再加最后一个数之后的这两段区间并起来的区间个数在 * ( k-1 )
		// 再加上最前面和最后面的区间个数
	}
//	printf ( "%d\n",ans );
	printf ( "%lld",( change ( n*m%mod )*gs%mod+mod-ans )%mod );
	//最后在用所有的可能减去 ans
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值