问题:跟定n个开区间(a,b)或闭区间[a,b],选择尽量多个区间,且这些区间互不相交。
贪心策略:将每个区间按右端点从小到大排序。遍历所有的区间,如果当前区间的左端点和最长区间的右端点相冲突,直接放弃,否则选取。
模版题
AC代码
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
struct qj{
int s,f;
friend bool operator<(qj x,qj y){
if(x.f==y.f) return x.s<y.s;
return x.f<y.f;
}
}a[maxn];
int n,ans,last;
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].s>>a[i].f;
sort(a+1,a+n+1);
last=1,ans=1;
for(int i=2;i<=n;i++){
if(a[i].s>=a[last].f){
++ans;
last=i;
}
}
cout<<ans;
return 0;
}
这题我们根据小岛和半径确定区间的左端点和右端点。然后套模版选择区间。但这道题有一个小坑是如果半径d<y,是覆盖不到的,输出-1。
AC代码:
cpp
#include<bits/stdc++.h>
using namespace std;
int n,d,c;
struct node{
double s,f;
friend bool operator<(node a,node b){
return a.f<b.f;
}
}pos[1005];
int main(){
cin>>n>>d;
while(n&&d){
++c;
bool flag=0;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
if(y>d) flag=1;
pos[i].s=x*1.0-sqrt(d*d-y*y);
pos[i].f=x*1.0+sqrt(d*d-y*y);
}
if(flag){
cout<<"Case "<<c<<": "<<-1<<endl;
cin>>n>>d;
continue;
}
sort(pos+1,pos+n+1);
int last=0,ans=0;
pos[last].f=-1e9;
for(int i=1;i<=n;i++){
if(pos[i].s>pos[last].f){
++ans;
last=i;
}
}
cout<<"Case "<<c<<": "<<ans<<endl;
cin>>n>>d;
}
return 0;
}
这题题目明确给出了不相交区间,我们不难想到模版题。但是这道题需要转一下弯:这里的区间是怎么构成的呢?
拿数据举例:
bash
4 2
2 1 0 3
找权值为2的区间,我们不难想到前缀和的思想。开一个数组s,s[t]存储a[1]^ a[2] ^ ... ^ a[t]的结果。找权值为2的区间[l,r]就是找s[l-1]^s[r]=2的l,r。
why? 因为同一个数异或偶数次,结果为0,s[r]的计算过程中有s[l-1]的计算,再异或一次s[l-1],就抵消了a[1] ^ ... ^ a[l-1]的结果。s[l-1] ^ s[r]求到的就是a[l]^ ... ^a[r]的结果。
那怎么找满足s[l-1]^s[r]=k的区间[l,r]呢?我们按下标顺序枚举左端点,计算每个左端点找到与之匹配的右端点的条件:s[r]=s[l-1] ^ k。
我们一步步拆解来说:
1:s[r]=s[l-1] ^ k是什么?
要找s[l-1] ^ s[r]=k,等号两边同时异或s[l-1],得到的就是s[r]=k^s[l-1],我们枚举l,s[l-1]已知,k已知,就看如果s[r]=k ^ s[l-1]说明[l,r]权值为k。
2:如何使用选择尽可能多不相交的区间?
| a数组下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素值 | 0 | 2 | 1 | 0 | 3 |
| s数组下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素值 | 0 | 2 | 3 | 3 | 0 |
我们另开一个数组h,初始化h数组元素为-1。当s[l]^k=i时,h[i]=l。这样当找到s[r]=i时,我们就可以通过数组h找到l,区间[l+1,r]就是一个满足权值为k的区间。另外,我们开个变量last(初始化为-1)记录上一个满足条件的区间右端点的值。如果当前区间满足条件,且左端点比上一个满足条件的区间的右端点要大说明两个区间不相交,答案+1。
举例来说,变量i遍历s数组,
· i=0,s[i]=0,h[0]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[2]=0;
· i=1,s[1]=2,h[2]=0,匹配到了左端点为h[2]+1,然后看一下左端点的值是否大于last,是则ans++,last=i。然后记录h[0]=1;
| h数组下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素值 | 1 | -1 | 0 | -1 | -1 |
· i=2,s[i]=3,h[3]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[1]=2;
· i=3,s[i]=3,h[3]=-1,说明暂时没有匹配到满足的左端点,不处理。然后记录h[1]=3;
h数组:
| h数组下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素值 | 1 | 3 | 0 | -1 | -1 |
· i=4,s[i]=0,h[0]=1,匹配到了左端点为h[0]+1,大于last,ans++,last=i。于是找到了两个不相交的区间。记录h[2]=4。
h数组:
| h数组下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 元素值 | 1 | 3 | 4 | -1 | -1 |
3:在代码实现上要注意: a<=2^20,s数组要开到2e6比较保险。
AC代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+5;
int n,k,a[maxn],s[maxn],h[maxn];
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++) {
cin>>a[i];
s[i]=s[i-1]^a[i];
}
for(int i=0;i<=n;i++)h[i]=-1;
h[0^k]=1;
int last=0,ans=0;
for(int i=1;i<=n;i++){
if(h[s[i]]!=-1&&h[s[i]]>last) {
++ans;
last=i;
}
h[s[i]^k]=i+1;
}
cout<<ans;
return 0;
}