【随缘更(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; } }