【随缘更(gu)】牛客D4简要思路(没有题解)

T1

当然不能枚举每个区间,于是我们考虑算贡献。

对于每个位置i,我们计算其作为区间内第一个出现ai的位置的区间总数,则有ans=sigma( i - last[i] ) * ( n - i + 1 ) ,n为区间长度,last[i]为上一个出现ai的位置。

(其实还有一种简单的递推思路,定义f[i]为前 i 个数的权值和,f[i] = fi[-1] + i - last[i] ,可是我太菜了,没想清楚以为是错的Orz)

这样我们解决了k=1的情况。

然后我们考虑k不等于1

如果我们复制第一段区间插在后面,定义第一段区间的权值和为C1,前两段为C2,则第二段对这两段的贡献delta=C2-C1,而且显然 第三段对第二、三两段的贡献也为delta,后面的同理。

这样我们解决了相邻复读区间的贡献。

对于不相邻的复读区间,由于跨一整个区间,则每个区间的权值都为tot,计算这些区间的总数即可。

代码:

#include <cstdio>
#include <map>
#include <iostream>
 
#define int long long int
 
using namespace std;
 
const int mod=1e9+7;
 
inline int read() {
    int x=0,f=1;
    char cr=getchar();
    while (cr>'9' || cr<'0') {
        if (cr=='-') f=-1;
        cr=getchar();
    }
    while (cr>='0' && cr<='9') {
        x=(x<<3)+(x<<1)+cr-'0';
        cr=getchar();
    }
    return x*f;
}
 
const int maxn=1000500;
 
map<int,int> loc;
 
int a[maxn],last[maxn];
 
inline int sigma(int k) {
	int x=k,y=k+1;
	if (x&1) return (y/2*x)%mod;
	return (x/2*y)%mod;
}
signed main() {
    int n=read(),k=read();
    int tot=0;
    for (int i=1;i<=n;i++) a[i]=read();
	for (int i=1;i<=n;i++) {
		if (!loc[a[i]]) tot++;
		last[i]=loc[a[i]],loc[a[i]]=i;
	}
	if (k==1) {
	    int ans=0;
		for (int i=1;i<=n;i++) ans+=(i-last[i])*(n-i+1),ans%=mod;
	    printf("%lld",ans);
	    return 0;
	}
	else {
		int cont1=0,cont2=0;
		for (int i=1;i<=n;i++) cont1+=(i-last[i])*(n-i+1),cont1%=mod;
		for (int i=n+1;i<=2*n;i++) a[i]=a[i-n],last[i]=loc[a[i]],loc[a[i]]=i;
		for (int i=1;i<=2*n;i++) cont2+=(i-last[i])*(2*n-i+1),cont2%=mod;
		int delt=cont2-cont1+mod;delt%=mod;
		int ans=cont2;
		ans+=(k-2)*(delt),ans%=mod;
		ans+=(sigma(k-2)*n%mod*n%mod*tot%mod)%mod,ans%=mod;
	    printf("%lld",ans);
	    return 0;
	}
}

  

猜你喜欢

转载自www.cnblogs.com/YoOXiii/p/11804464.html