HDU-7314 2023“钉耙编程”杭电多校赛(4)Simple Set Problem
题目大意
有 k k k个非空的多重集合,第 i i i个集合的大小为 c i c_i ci,你需要在每个多重集合中选一个数,组成长度为 k k k的数组 a a a。
设 d = max i = 1 n a i − min i = 1 n a i d=\max\limits_{i=1}^na_i-\min\limits_{i=1}^na_i d=i=1maxnai−i=1minnai,求 d d d的最小值。
有 T T T组数据。
1 ≤ T ≤ 1 0 6 , 1 ≤ k ≤ 1 0 6 1\leq T\leq 10^6,1\leq k\leq 10^6 1≤T≤106,1≤k≤106
数据保证每个测试用例中 ∑ i = 1 k c i \sum\limits_{i=1}^kc_i i=1∑kci不超过 1 0 6 10^6 106,所有测试用例的 ∑ i = 1 k c i \sum\limits_{i=1}^kc_i i=1∑kci的总和不超过 4 × 1 0 6 4\times 10^6 4×106。
题解
将 k k k个集合的数以结构体的方式放在一起,每个数有一个值 a a a和所属集合 b b b。将这些数按 a a a从小到大排序,再用双指针来求答案。每次第一个指针往后移一个数,第二个指针移动到保证两个指针之间存在每一个集合的数的第一个位置,这个可以利用 b b b和一个桶来处理。最后,计算双指针中最大值和最小值之差。因为这些数是排好序的,所以用第二个指针的位置的值减第一个指针的位置的值即可。将所有最大值和最小值之差求最小值即为答案。
设 n n n为 k k k个集合的数的总和,则时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
using namespace std;
int T,n,w1,sum,hd,tl,ans,z[1000005];
struct node{
int v,cl;
}w[1000005];
bool cmp(node ax,node bx){
return ax.v<bx.v;
}
int in(){
int t=0,fl=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fl=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
t=t*10+ch-'0';ch=getchar();
}
return t*fl;
}
int main()
{
T=in();
while(T--){
w1=sum=0;
ans=2e9;
n=in();
for(int i=1,k;i<=n;i++){
z[i]=0;
k=in();
for(int j=1;j<=k;j++){
w[++w1].v=in();
w[w1].cl=i;
}
}
sort(w+1,w+w1+1,cmp);
for(hd=1,tl=0;hd<=w1;hd++){
while(tl+1<=w1&&sum<n){
++tl;
++z[w[tl].cl];
if(z[w[tl].cl]==1) ++sum;
}
if(sum<n) break;
ans=min(ans,w[tl].v-w[hd].v);
--z[w[hd].cl];
if(z[w[hd].cl]==0) --sum;
}
printf("%d\n",ans);
}
return 0;
}