题目链接:http://poj.org/problem?id=2528
分析:其实离散化就是将一个很大的区间映射为一个很小的区间,而不改变原有的大小覆盖关系。但是注意简单的离散化可能 会出现错误,给出下面两个简单的例子应该能体现普通离散化的缺陷: 例子一:1-10 1-4 5-10 例子二:1-10 1-4 6-10 普通离散化后都变成了[1,4][1,2][3,4] 线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢? 例子一是完全被覆盖掉了,而例子二没有被覆盖 解决的办法则是对于距离大于1的两相邻点,中间再插入一个点,本题还用到了Lazy标记的思想。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e4+10;
int s[maxn],e[maxn],lisan[4*maxn],t,n,tmp,vis[maxn],ans;
struct node{
int l,r,v;
}tree[16*maxn];
int bin(int x){
int l=1,r=tmp-1;
while(l<=r){
int mid=(l+r)>>1;
if(lisan[mid]==x) return mid;
else if(x<lisan[mid]) r=mid-1;
else l=mid+1;
}
}
void build(int l,int r,int id){
tree[id].l=l,tree[id].r=r,tree[id].v=-1;
if(l==r) return ;
int mid=(l+r)>>1;
build(l,mid,2*id);
build(mid+1,r,2*id+1);
}
void update(int l,int r,int id,int col){
if(l<=tree[id].l && tree[id].r<=r){
tree[id].v=col;
return ;
}
if(tree[id].v!=-1) {
tree[2*id].v=tree[2*id+1].v=tree[id].v;
tree[id].v=-1;
}
int mid=(tree[id].l+tree[id].r)>>1;
if(r<=mid) update(l,r,2*id,col);
else if(l>mid) update(l,r,2*id+1,col);
else update(l,mid,2*id,col),update(mid+1,r,2*id+1,col);
}
void query(int l,int r,int id){
if(tree[id].v!=-1){
if(!vis[tree[id].v]){
ans++;
vis[tree[id].v]=1;
}
return ;
}
if(l==r) return ;
int mid=(l+r)>>1;
query(l,mid,2*id);
query(mid+1,r,2*id+1);
}
int main(){
///std::ios::sync_with_stdio(false);
cin>>t;
while(t--){
memset(vis,0,sizeof(vis));
cin>>n;
for(int i=1;i<=n;i++) cin>>s[i]>>e[i];
for(int i=1;i<=n;i++) lisan[i]=s[i];
for(int i=n+1;i<=2*n;i++) lisan[i]=e[i-n];
///离散化
sort(lisan+1,lisan+2*n+1);
int tot=1;
lisan[tot++]=lisan[1];
for(int i=1;i<2*n;i++)
if(lisan[i+1]!=lisan[i]) lisan[tot++]=lisan[i+1];
tmp=tot-1;
for(int i=1;i<tot;i++)
if(lisan[i+1]-lisan[i]!=1) lisan[++tmp]=lisan[i]+1;
sort(lisan+1,lisan+tmp);
build(1,tmp-1,1);
for(int i=1;i<=n;i++){
int L=bin(s[i]),R=bin(e[i]); //二分
update(L,R,1,i);
}
query(1,tmp-1,1);
cout<<ans<<endl;
ans=0;
}
}
线段树的应用
维护区间最大值
void pushup( ll r ) {
root[r] = max(root[ls],root[rs]);
}
void updata( ll r, ll le, ll ri, ll pos, ll w ) {
if( le == ri ) {
root[r] = w;
return;
}
ll mid = (le+ri)/2;
if( pos <= mid ) {
updata(ls,le,mid,pos,w);
}
if( mid < pos ) {
updata(rs,mid+1,ri,pos,w);
}
pushup(r);
}
去重+离散化
//一个n*n的图中有效贡献点有限,可这样离散,注意要写行列贡献相同需去重
for( ll i = 1; i <= n; i ++ ) {
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].w);
dis[i] = 0, num[i] = a[i].y;
}
sort(num+1,num+n+1);
sz = unique(num+1,num+n+1) - (num+1); //去重
sort(a+1,a+n+1,cmp);
a[n+1].x = a[n].x + 1;
build(1,1,sz); //建树
求图贡献最大
//一段区间最大贡献
ll query( ll r, ll le, ll ri, ll L, ll R ) {
if( R < le || ri < L ) {
return 0;
}
if( L <= le && ri <= R ) {
return root[r];
}
ll MAX = 0;
ll mid = (le+ri)/2;
if( L <= mid ) {
MAX = max(MAX,query(ls,le,mid,L,R));
}
if( mid < R ) {
MAX = max(MAX,query(rs,mid+1,ri,L,R));
}
return MAX;
}
dis[i] = query(1,1,sz,1,le-1) + a[i].w;
if( a[i].x != a[i+1].x ) {
if( dis[i] > root[pos[le]] ) {
updata(1,1,sz,le,dis[i]);
}
for( ll i = 1; i <= cnt; i ++ ) {
if( dt[i] > root[pos[st[i]]] ) {
updata(1,1,sz,st[i],dt[i]);
}
}
cnt = 0;
} else {
cnt ++;
st[cnt] = le, dt[cnt] = dis[i];
}
ans = max(ans,dis[i]);
区间最大值
void build(int l, int r, int n)
{
s[n].l = l;
s[n].r = r;
s[n].maxx = 0;
s[n].minn = N;
if(l == r)
{
s[n].maxx = s[n].minn = a[l];
return;
}
int mid = (l + r) >> 1;
build(l, mid, n<<1);
build(mid + 1, r, n<<1|1);
s[n].maxx = max(s[n<<1].maxx, s[n<<1|1].maxx);
s[n].minn = min(s[n<<1].minn, s[n<<1|1].minn);
}
void query(int l, int r, int n)
{
if(s[n].l == l && s[n].r == r)
{
ans_x = max(ans_x, s[n].maxx);
ans_y = min(ans_y, s[n].minn);
return;
}
int mid = (s[n].l + s[n].r) >> 1;
if(r <= mid)
query(l, r, n<<1);
else if(l > mid)
query(l, r, n<<1|1);
else
{
query(l, mid, n<<1);
query(mid+1, r, n<<1|1);
}
}