YbtOJ 冲刺 NOIP2021 模拟赛 B 组 Day4 B. 循环数组【数学】

102 篇文章 2 订阅
40 篇文章 1 订阅
这篇博客探讨了一种在线性时间内解决数列中每个数贡献的算法。通过巧妙的数学公式化简和编程实现,可以在O(n)的时间复杂度内计算出答案。文章详细解释了思路,包括如何对每个数进行考虑,以及如何处理特殊情况。代码部分使用C++实现,包括对数列的预处理、计算每个数的贡献,并最终输出结果。
摘要由CSDN通过智能技术生成

题目

在这里插入图片描述

思路

这道题我们对每个数考虑贡献。
设last为上一个当前数出现的位置。
显然可得: ( i − l a s t i − 1 ) + ( n − i ) + ( i − l a s t i − 1 ) ∗ ( n − i ) + 1 (i-last_i-1)+(n-i)+(i-last_i-1)*(n-i)+1 (ilasti1)+(ni)+(ilasti1)(ni)+1
因式分解可得:
( i − l a s t i ) ( n − i + 1 ) (i-last_i)(n-i+1) (ilasti)(ni+1)
把范围扩大到 n × k n\times k n×k
那我们可以把相同相对位置的数一起算:
( i − l a s t i ) ( k n − i + 1 ) + ( i − l a s t i ) ( k n − i − n + 1 ) + … + ( i − l a s t i ) ( k n − i − 2 × n + 1 ) (i-last_i)(kn-i+1)+(i-last_i)(kn-i-n+1)+…+(i-last_i)(kn-i-2\times n+1) (ilasti)(kni+1)+(ilasti)(knin+1)++(ilasti)(kni2×n+1)
化简得:
( i − l a s t i ) ( k 2 n − k i − n × k ( k − 1 ) 2 + k ) (i-last_i)(k^2n-ki-n\times \displaystyle\frac{k(k-1)}{2}+k) (ilasti)(k2nkin×2k(k1)+k)
这样就可以 O ( n ) O(n) O(n) 求答案.。
注意特判第一个区间的情况。

代码

#include<algorithm>
#include<iostream>
#include<cstring> 
#include<cstdio>
using namespace std;
const long long mod=1e9+7;
long long n,k,a[1000010],b[1000010],a2[1000010];
long long v[1000010],last[1000010];
long long ans;
int main()
{
	freopen("loop.in","r",stdin);
	freopen("loop.out","w",stdout);
	scanf("%lld%lld",&n,&k);
	for(long long i=1; i<=n; i++)
	 {
	   scanf("%lld",&a[i]);
	   b[i]=a[i];
	 }
    sort(b+1,b+1+n);
    long long qcn=unique(b+1,b+1+n)-b-1;
    for(long long i=1; i<=n; i++)
       a[i]=lower_bound(b+1,b+1+qcn,a[i])-b;
    for(long long i=1; i<=n; i++)
       a2[i]=a[i];
    for(long long i=1; i<=n; i++)
       a2[n+i]=a2[i];
    for(long long i=1; i<=n*2; i++)
     {
     	if(v[a2[i]])
     	  last[i]=v[a2[i]];
		v[a2[i]]=i;
	 }
	for(long long i=1; i<=n; i++)
	   ans=(ans+((i+n-last[i+n]+mod)%mod*(((n*(k-1)%mod*(k-1))%mod-i*(k-1)%mod-n*((((k-1)*(k-2)/2))%mod)+k-1+mod))%mod)%mod)%mod;
	memset(v,0,sizeof(v));
	memset(last,0,sizeof(last));
	for(long long i=1; i<=n; i++)
     {
     	if(v[a[i]])
     	  last[i]=v[a[i]];
		v[a[i]]=i;
	 }
	for(long long i=1; i<=n; i++)
	   ans=(ans+(i-last[i])*(n*k-i+1)%mod)%mod;
	printf("%lld",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值