UVA1723 Intervals题解
题目传送门。
这里讲解贪心加树状数组的做法,如果想看差分约束的做法,请左转至我的差分约束系统学习笔记。
首先容易想到可以将所有的区间先按照 \(x\) 坐标从小到大排序,然后相同的再按照 \(y\) 坐标从小到大排序,你会发现想使所选的数尽量少,只需要每次在一个区间选尽量靠后的就行了,因为这样才有可能被其他的区间利用,从而减少要选的数的数量,然而这样时间复杂度是 \(O(nv)\) 的,会 TLE,于是我们想到每次在一个区间查找有多少个数被选了可以使用树状数组优化,于是时间复杂度变成了 \(O(n \log v)\),因为选数不会选重复的数,所以选数部分均摊下来时间复杂度是 \(O(v)\) 的,再加上每次在一个区间查找有多少个数的 \(O(n \log v)\),得到 \(O(n \log v+v)\),然而在这里 \(v\) 和 \(n\) 是同阶的,所以可以舍去 \(v\),变成 \(O(n \log v)\)。
注意到题目的输出有一句话:在除了最后一组数据后输出空行。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+5;
struct node
{
int x;
int y;
int z;
}a[N];
int vis[N];
int cmp(node x,node y)
{
return x.y == y.y?x.x>y.x:x.y<y.y;
}
int t[N];
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int ans = 0;
int n,m;
memset(vis,0,sizeof(vis));
memset(t,0,sizeof(t));
scanf("%d",&n);
for(int i = 1;i<=n;i++)
{
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].z);
}
sort(a+1,a+n+1,cmp);
for(int i = 1;i<=n;i++)
{
int k = 0;
int x = a[i].y;
while(x)
{
k+=t[x];
x-=x&(-x);
}
x = a[i].x-1;
while(x)
{
k-=t[x];
x-=x&(-x);
}
if(k>=a[i].z)
{
continue;
}
for(int j = a[i].y;j>=a[i].x;j--)
{
if(vis[j] == 0)
{
vis[j] = 1;
int x = j;
while(x<=50000)
{
t[x]++;
x+=x&(-x);
}
k++;
ans++;
if(k == a[i].z)
{
break;
}
}
}
}
printf("%d\n",ans);
if(_)//本题输出很毒瘤,请注意
{
printf("\n");
}
}
return 0;
}
各种各样的多倍经验:
- SP116 INTERVAL - Intervals(直接复制即可)
- AT_abc216_g [ABC216G] 01Sequence(稍加改动)
- P1250 种树(可以不用树状数组)
- P10934 西瓜种植(可以不用树状数组)
- P1986 元旦晚会(可以不用树状数组)

浙公网安备 33010602011771号