LG11012
首先我们要对 \(\sum_{i=1}^W \sum_{j=i+1}^W \min(c_i,c_j)\) 进行化简。具体地,一个 \(c_i\) 会产生贡献,当且仅当存在一个 \(c_j\) 满足 \(c_j \ge c_i\)。设这样的 \(j\) 的个数为 \(d\) 个,那么所产生的贡献为 \(c_i \times d\)。再枚举每个区间,这样就得到一个 \(O(n^2W)\) 的做法,但无法通过本题。
事实上,这样枚举会出现很多冗余。比如一个区间 \([i,j]\) 的绚丽程度已经不小于 \(k\),那么 \([i,j+1],[i,j+2],\cdots,[i,n]\) 的绚丽程度都一定是不小于 \(k\) 的(出现次数增加,对应的贡献也会增加),但长度显然是前者更优,因此固定 \(i\),\(j\) 不断右移,一旦出现合法的区间,更新并退出即可。
继续推下去,假设 \([i,j]\) 是以 \(i\) 为左端点的最短的合法区间,那么 \([i,j-1]\) 一定是不合法的(出现次数减少,对应的贡献也会减少),即 \([i+1,j-1]\) 一定也是不合法的(原因同上),那么下一个左端点 \(i+1\) 确定右端点时直接从上一次的 \(j\) 开始枚举即可。显然,\(i,j\) 都是只向右移,每个数都被 \(i,j\) 各扫到一次。
接下来考虑如何快速计算一个颜色 \(a\) 进入或离开区间时的贡献。根据上文的结论,该贡献为 \(\sum_{i=1}^W [c_i \ge c_a]\times[i\neq a]\)。直接使用树状数组维护即可,具体实现时需要注意边界情况。
总时间复杂度 \(O(n\log W)\),代码如下:
#include <iostream>
#include <cstdio>
#define int long long
#define MOD 1000000007
#define W 2000000
using namespace std;
int n,k,a[2000001],c[2000001],rk[2000001],rev[2000001],sum,ans,flag;
int t[8000001];
int lowbit( int x )
{
return x & ( -x );
}
void upd( int x , int y )
{
// cout << x << ' ' << y << endl;
for( int i = x ; i <= W ; i += lowbit( i ) )
t[i] += y;
return;
}
int que( int x )
{
int sum = 0;
for( int i = x ; i >= 1 ; i -= lowbit( i ) )
sum += t[i];
return sum;
}
void add( int x )
{
int gs = que( W ) - que( c[x] );//这里先查询后修改能够规避上文 i = a 的情况
if( c[x] ) upd( c[x] , -1 );
c[x] ++;
upd( c[x] , 1 );
sum += gs;
}
void del( int x )
{
int gs = que( W ) - que( c[x] - 1 ) - 1;//减 1 是为了去除自身
upd( c[x] , -1 );
c[x] --;
if( c[x] ) upd( c[x] , 1 );
sum -= gs;
return;
}
signed main()
{
cin >> n >> k;
ans = 0x3f3f3f3f;
for( int i = 1 ; i <= n ; i ++ )
cin >> a[i];
int i = 1,j = 0;
while( i <= n )
{
// cout << i << ' ' << j << endl;
while( j < n && sum < k ) j ++,add( a[j] );
// add( a[j] );
if( sum < k ) break;
// cout << i << ' ' << j << ' ' << sum << ' ' << c[2] << endl;
ans = min( ans , j - i + 1 );
del( a[i] );
i ++;
}
if( ans == 0x3f3f3f3f ) cout << -1;
else cout << ans;
return 0;
}

浙公网安备 33010602011771号