【USACO13JAN】牛的阵容Cow Lineup

原题:

首先贪一波,k种血统当然是尽可能删光啦

那么不考虑k种用不完的情况,答案对应的连续血统组成的区间一定是一个包含k+1种血统的区间

进一步需要发现,在所有的包含k+1种血统的区间中,只需考虑极大区间

即左右端点延申一格就由k+1变为k+2的区间

这个性质也易证

那么我们就可以用双指针,当右端点加入一个血统后,左端点向右移动直到区间内血统数小于k+2

即枚举右端点,然后考虑左端点最远能到哪里

接下来就是如何统计这些区间种数量最多的血统

一种方法是用权值线段树,或平衡树维护

但是其实还有性质可以挖掘

即没有必要统计数量最多的血统

只需将被枚举的右端点的血统的数量统计到答案即可

虽然极大的含k+1种的区间右端点不一定是数量最多的,但一定存在以数量最多的血统作为右端点的极大子区间

如果存在最多+非最多的情况,那么把右端的非最多删去,答案不会减少,甚至还可能增加(因为左端点可能还能再向左移动)

而枚举右端点的时候是逐个枚举的

所以只将右端点的数量统计保证不会遗漏答案

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 int n,m,a[110000];
 6 int b[110000],c[110000],ctp=0;
 7 int cnt[110000],nm=0;
 8 int bnrsch(int x){
 9     int l=1,r=ctp,md;
10     while(l+1<r){
11         md=(l+r)>>1;
12         (c[md]<x ? l : r)=md;
13     }
14     return c[l]==x ? l : r;
15 }
16 int main(){
17     cin>>n>>m;
18     for(int i=1;i<=n;++i){
19         scanf("%d",&a[i]);
20         b[i]=a[i];
21     }
22     sort(b+1,b+n+1);
23     b[0]=-1;
24     for(int i=1;i<=n;++i)if(b[i]!=b[i-1])  c[++ctp]=b[i];
25     for(int i=1;i<=n;++i)  a[i]=bnrsch(a[i]);
26     int ans=0;
27     for(int i=1,j=1;i<=n;++i){
28         ++cnt[a[i]];
29         if(cnt[a[i]]==1)  ++nm;
30         for(;nm>m+1;++j){
31             --cnt[a[j]];
32             if(!cnt[a[j]])  --nm;
33         }
34         ans=max(ans,cnt[a[i]]);
35         //cout<<j<<" "<<i<<endl;
36     }
37     cout<<ans<<endl;
38     return 0;
39 }
View Code
原文地址:https://www.cnblogs.com/cdcq/p/12153599.html