【2020牛客多校九:尺取】F :Groundhog Looking Dowdy

【传送门】F :Groundhog Looking Dowdy

【难度】

3 / 10 3/10 3/10
稍微想一想就能想到的尺取题

【题意】

一共有 n n n 天,每天有 k i k_i ki 条衣服,每条衣服有美丽值 a i , j a_{i,j} ai,j
你需要选择其中的 m m m 天,每天选择当天的一条衣服,使得选择出的 m m m 条衣服的魅力值的最大差值最小

【数据范围】

1 ≤ a i , j ≤ 1 0 9 1\le a_{i,j} \le 10^9 1ai,j109
1 ≤ n ≤ 1 0 6 1\le n \le 10^6 1n106
1 ≤ m ≤ n 1\le m \le n 1mn
∑ n   k i i = 1       ≤ 2   ⋅   1 0 6 \underset{i=1\,\,\,\,\,}{\overset{n}{\sum}\,k_i}\le2 \,·\,10^6 i=1nki2106

【样例输入】

n   m n\ m n m
k 1 a 1 , 1   , a 1 , 2 ,   ⋯   , a 1 , k 1 k_1 \quad a_{1,1}\,,a_{1,2},\,\cdots\,,a_{1,k_1} k1a1,1,a1,2,,a1,k1
⋮ \vdots
k n a n , 1   , a n , 2 ,   ⋯   , a n , k n k_n \quad a_{n,1}\,,a_{n,2},\,\cdots\,,a_{n,k_n} knan,1,an,2,,an,kn

4 3
1 3
2 8 6
1 2
3 1 7 5

【样例输出】

2

【解释】

第一天选择 3 的衣服,
第三天选择 2 的,
第四天选择 1 的。
最大差值为 2 ,是最小的差值。

【思路】

n n n 很大, 不能从每天选哪件衣服去考虑。
我们考虑魅力值的差值,从这个方面去分析。

(1)
简单地想一下,若我们去二分差值是否可行?
那我们还需枚举起点。
每天贪心选择符合条件的衣服。
若有一个起点满足当前差值 x x x 可行,则二分范围下取,否则上取。

复杂度: 起点 * (每天天数 * 每天的衣服数) * 二分的次数
时间复杂度 O ( n × ∑ n   k i i = 1       × log ⁡ 2 1 0 9 ) O(n \times \underset{i=1\,\,\,\,\,}{\overset{n}{\sum}\,k_i}\times \log_210^9) O(n×i=1nki×log2109)

容易看出,会TLE。

(2)
想到,每次起点分别枚举过来:
对于同一个起点,我们会计算多次从该起点开始的答案计数。

从经验得知,尺取法可以解决该类问题。
【2020牛客多校九:尺取】F :Groundhog Looking Dowdy_第1张图片
(网络上和书本上最经典的一张图片)

那么我们定义 A [ i ] = j A[i]=j A[i]=j
下标i 设置成离散化后的美丽值
值A[I] 设置成这条美丽值衣服的对应天数

我们先按美丽值递增排序。

然后,我们只要尺取,使得尺取范围内的不同天数数量 m m m
最后答案就是 满足要求的 ( 区间头美丽值 - 区间尾美丽值 ) 的最小值

【AC核心代码】

λ = ∑ n   k i i = 1       \lambda = \underset{i=1\,\,\,\,\,}{\overset{n}{\sum}\,k_i} λ=i=1nki
时间复杂度 O ( λ log ⁡ λ + λ ) O(\lambda\log \lambda + \lambda) O(λlogλ+λ)
(排序+尺取)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e6+50;

int enen;
struct Node{
    int tt;		/// 存美丽值
    int ii;		/// 存天数
}M[MAX];		/// 排序后即可离散化

bool cmp(Node ta,Node tb){	/// 按美丽值升序
    return ta.tt < tb.tt;
}

int shu[MAX];		/// 计算某个天数出现的次数

int main()
{
    int day,m;
    scanf("%d%d",&day,&m);
    for(int i=1;i<=day;++i){
        int n;scanf("%d",&n);
        while(n--){
            int t;scanf("%d",&t);
            enen++;
            M[enen].tt = t;
            M[enen].ii = i;
        }
    }
    
    sort(M+1,M+enen+1,cmp);
    
    int L = 0;
    int R = 0;
    int ans = INF;
    int sum = 0;
    while(1){				/// 标准尺取
        R++;
        while(R <= enen && sum < m){
            if(shu[M[R].ii]==0){
                sum++;
            }
            shu[M[R].ii]++;
            if(sum==m)break;
            R++;
        }
        if(R > enen)break;
        L++;
        while(L <= R && sum==m){
            ans = min(ans ,M[R].tt - M[L].tt);
            if(shu[M[L].ii]==1){
                sum--;
            }
            shu[M[L].ii]--;
            if(sum!=m)break;
            L++;
        }
    }
    printf("%d",ans);
    return 0;
}

这次敲得比较差,因为尺取手比较生,还有离散化也比较生疏,希望多多练习起来。

你可能感兴趣的:(算法)