problem:
给你n首歌,和每首歌的音调区间[a, b], a<=b
给你m个歌唱家,和每个歌唱家的音调区间[c, d]他最多可以唱k首歌, c<=d
一个歌唱家可以唱一首歌 iff c<=a<=b<=d
问能不能给出一种方案,使得每首歌都有歌唱家唱,能的话输出YES和每首歌由哪个歌唱家唱[1, m];不能输出NO
1<=n, m<=1e5
1<=a, b, c, d, k<=1e9
think:
1.对于a,b,c,d可以离散化,这样最后有2*n+2*m种音调。防止爆long long,k超多n的变成n
2.离线,按照音调左区间排序,当处理第i首歌的时候,把所有的cj<=ai的歌唱家都已经放进我们维护的数据结构,这样就不用考虑左区间了,只找右区间大于当前歌曲的歌唱家。
3.于是我们要维护每个右区间位置的歌唱家的信息。
4.如第一个样例,有5个音调值 1, 2, 3, 4, 5,开始的时候每个音调都是0,
1 4 2 => (0, 0, 0, 2, 0)
1 3 => (0, 0, 0, 1, 0) //这一步找到>=3的最小的不是0的位置,是4
2 5 1 => (0, 0, 0, 1, 1)
2 4 => (0, 0, 0, 0, 1)
3 5 => (0, 0, 0, 0, 0)
5.上一步明显可以随便维护,比如树状数组。但是这里有个问题。题目要求输出每首歌的歌唱家。所以用vector维护那些值分别是哪个歌唱家贡献的。
code:
const int N = 100111;
struct point{
int a, b, c, d;
}p1[N], p2[N];
int n, m, cnt;
int li[N<<2];//用于离散化
LL C[N<<2];//树状数组
int ans[N];
vector<PII >vec[N<<2];
bool cmp(point x, point y){
return x.a < y.a;
}
inline int low_bit(int x){
return (x & (-x));
}
void update(int x, int v){
for(; x <= cnt; x += low_bit(x)) C[x] += v;
}
LL ask(int x){
LL ans = 0;
for(; x > 0; x -= low_bit(x)) ans += C[x];
return ans;
}
void add(int id){//加入歌唱家的信息
update(p2[id].b, p2[id].c);
vec[p2[id].b].PB(MP(p2[id].d, p2[id].c));
}
int mid_find(int fr){
LL pre = ask(fr - 1);
if(ask(cnt) - pre <= 0) return -1;
int L = fr, R = cnt;
while(L < R){
int mid = (L + R) >> 1;
LL val = ask(mid) - pre;
if(val == 0) L = mid + 1;
else {
val = ask(mid - 1) - pre;
if(val == 0) return mid;
else R = mid - 1;
}
}
return L;
}
int query(int id){
int mi = mid_find(p1[id].b);
if(mi == -1) return -1;
update(mi, -1);
PII tmp = vec[mi][vec[mi].size()-1];
vec[mi].pop_back();
ans[p1[id].c] = tmp.fi;
tmp.se--;
if(tmp.se > 0) vec[mi].PB(tmp);
}
int main(){
while(scanf("%d", &n) != EOF){
cnt = 0;
for(int i = 1; i <= n; ++i){
scanf("%d%d", &p1[i].a, &p1[i].b);
p1[i].c = i;
li[cnt++] = p1[i].a;
li[cnt++] = p1[i].b;
}
scanf("%d", &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d%d", &p2[i].a, &p2[i].b, &p2[i].c);
if(p2[i].c > n) p2[i].c = n;
p2[i].d = i;
li[cnt++] = p2[i].a;
li[cnt++] = p2[i].b;
}
sort(li, li +cnt);
int j = 1;
for(int i = 1; i < cnt; ++i){
if(li[i] > li[i-1]) li[j++] = li[i];
}
cnt = j;
for(int i = 0; i <= cnt; ++i) vec[i].clear();
for(int i = 0; i <= cnt; ++i) C[i] = 0;
for(int i = 1; i <= n; ++i){
p1[i].a = lower_bound(li, li + cnt, p1[i].a) - li + 1;
p1[i].b = lower_bound(li, li + cnt, p1[i].b) - li + 1;
}
for(int i = 1; i <= m; ++i){
p2[i].a = lower_bound(li, li + cnt, p2[i].a) - li + 1;
p2[i].b = lower_bound(li, li + cnt, p2[i].b) - li + 1;
}
sort(p1 + 1, p1 + n + 1, cmp);
sort(p2 + 1, p2 + m + 1, cmp);
bool has = 1;
for(int i = 1, j = 0; i <= n; ++i){
while(j + 1 <= m && p2[j+1].a <= p1[i].a){
++j;
add(j);
}
int id = query(i);
if(id == -1) {
has = 0;
break;
}
}
if(has){
puts("YES");
for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
} else {
puts("NO");
}
}
return 0;
}
本文探讨了一种算法,用于解决将特定音调区间的歌曲分配给歌唱家的问题,确保每首歌曲都有歌唱家演唱。通过离散化音调区间、离线排序和维护歌唱家信息,算法有效地匹配歌曲与歌唱家,实现高效解决方案。
1500

被折叠的 条评论
为什么被折叠?



