题意:
给你n个点,他们1到n依次从左到右连接。问你m对数(A,B)。AB不能相连。
问你最少要拆多少条边.
POINT:
O(m)的做法:
记录每一个左端点,与他有争议的最近的右端点。记为a数组(可以贪心的知道断每一个右端点是最优的)。
那么假设断的是a[1],如果往后扫的有争议的右端点(比如a[2])比a[1]大,那么就只要断a[1]就行了。
如果比a[1]小,那么断a[2]。
用了输入挂,必须文件输入
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <map>
#include <math.h>
using namespace std;
#define LL long long
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int main()
{
int n,m;
int* a = new int[(int)1e7 + 10];
n=read();
m=read();
// cin>>n>>m;
int M=1e7+7;
for(int i=1;i<=n;i++)
a[i]=0;
for(int i=1;i<=m;i++){
int l,r;
l=read();
r=read();
// cin>>l>>r;
if(r<l) swap(r, l);
M=min(M,l);
if(a[l]!=0&&a[l]>r) a[l]=r;
else if(a[l]==0) a[l]=r;
}
int ans=1;
int k=a[M];
for(int i=1;i<=n;i++){
if(a[i]==0) continue;
if(i>=k){
ans++;
k=a[i];
}else if(k>a[i]){
k=a[i];
}
}
if(m==0) printf("0\n");
else
printf("%d\n",ans);
return 0;
}
m*log(m)的做法:
R端点从左到右排序。然后看看L到R有没有拆至少一条边,有的话就不用拆了。
没有的话拆R这个点的边。
看L到R有没有拆边可以用树状数组实现log(N)的效率。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <map>
using namespace std;
#define LL long long
const int maxn = 1e6+33;
const int mod = 1e9+7;
int num[maxn];
struct node
{
int l,r;
bool friend operator < (node a,node b){
return a.r<b.r;
}
}q[maxn];
int n,m;
void add(int x,int p)
{
for(int i=x;i<=n;i+=i&-i)
num[i]+=p;
}
int query(int x)
{
int ans=0;
for(int i=x;i>=1;i-=i&-i){
ans+=num[i];
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
}
sort(q+1,q+1+m);
int ans=0;
for(int i=1;i<=m;i++){
int to = query(q[i].r)-query(q[i].l);
if(to==0){
add(q[i].r,1);
ans++;
}
}
printf("%d\n",ans);
return 0;
}