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;
}
posted @ 2024-02-23 22:05  maple276  阅读(19)  评论(0编辑  收藏  举报