CF1552E: Colors and Intervals 题解
E:
题意:(妙妙构造)n*k的数列a,n种颜色每种k个。你需要每种颜色选两个位置,并覆盖这两个位置的区间。要求最终所有位置被覆盖的次数不超过 \(\lceil\frac n{k-1}\rceil\)。请你构造方案。
这个 \(\lceil\frac n{k-1}\rceil\) 长的很抽象,没发现什么性质,让人很头痛。我们先想想一般性的思路。
我们一般会贪心选靠前的区间,当覆盖次数超过上限时再寻找下一个区间,但是如果我们按右区间排序,左端会乱跳,不太好统计最大覆盖次数。
我们可以不考虑具体如何覆盖,如果一个区块内我们只选了 \(\lceil\frac n{k-1}\rceil\) 个区间,那么无论这些区间怎么排列都不会超过覆盖上限。那么我们就每选出 \(\lceil\frac n{k-1}\rceil\) 个区间就分一个块。从块儿的右边再选区间。
Solution:
设 \(c_{i,j}\) 为第 i 种颜色第 j 次出现的位置。
我们先把 \(c_{1,2},~c_{2,2},~c_{3,2}~...~c_{n,2}\) 排序,取前 \(\lceil\frac n{k-1}\rceil\) 个颜色, \(c_{i,2}\) 作为右端点,\(c_{i,1}\) 为左端点,把这些颜色的答案确定下来。
接着再把 \(c_{1,3},~c_{2,3},~c_{3,3}~...~c_{n,3}\) 排序,在答案还未确定的颜色中选前 \(\lceil\frac n{k-1}\rceil\) 个颜色,\(c_{i,3}\) 作为右端点,\(c_{i,2}\) 作为左端点,把这些颜色答案确定下来。
你会发现第二次选的 \(\lceil\frac n{k-1}\rceil\) 个区间和第一次选的 \(\lceil\frac n{k-1}\rceil\) 个区间是没有重叠的(第一次选的最后一个颜色的右端点一定小于第二次的任意左端点),相当于分别在两个独立的块儿里。
一直这样排序,你会发现我们一共选了 \(k-1\) 次,每次确定 \(\lceil\frac n{k-1}\rceil\) 个颜色的答案,所以 \(k-1\) 次之后所有的颜色答案都确定下来了。
int T;
int n,k;
int ans[N],a[N];
int lst[N],tim[N],L[N];
struct E{
int r,id;
}d[N];
inline bool cmp(E A,E B) { return A.r < B.r; }
vector <E> g[N];
int main() {
n = read(); k = read();
for(int i=1;i<=n*k;i++) {
a[i] = read();
tim[a[i]]++;
if(tim[a[i]]>1) g[tim[a[i]]].push_back({i,a[i]});
if(lst[a[i]]) L[i] = lst[a[i]];
lst[a[i]] = i;
}
int shang = ceil((double)n/(double)(k-1.0));
for(int i=2;i<=k;i++) {
sort(g[i].begin(),g[i].end(),cmp);
int ci = 0;
for(int j=0;j<n;j++) {
if(ans[g[i][j].id]) continue;
ans[g[i][j].id] = g[i][j].r;
ci++;
if(ci==shang) break;
}
}
for(int i=1;i<=n;i++) cout<<L[ans[i]]<<" "<<ans[i]<<endl;
return 0;
}