【传送门】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
1≤ai,j≤109
1
≤
n
≤
1
0
6
1\le n \le 10^6
1≤n≤106
1
≤
m
≤
n
1\le m \le n
1≤m≤n
∑
n
k
i
i
=
1
≤
2
⋅
1
0
6
\underset{i=1\,\,\,\,\,}{\overset{n}{\sum}\,k_i}\le2 \,·\,10^6
i=1∑nki≤2⋅106
【样例输入】
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=1∑nki×log2109)
容易看出,会TLE。
(2)
想到,每次起点分别枚举过来:
对于同一个起点,我们会计算多次从该起点开始的答案计数。
从经验得知,尺取法可以解决该类问题。
(网络上和书本上最经典的一张图片)
那么我们定义
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=1∑nki
时间复杂度
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;
}
这次敲得比较差,因为尺取手比较生,还有离散化也比较生疏,希望多多练习起来。