题意:
共n段,俩操作
修改:第i段【li,ri】染成颜色i
仅最后一次查询:最后的颜色数
li ri<=1e7 n<=1e4
思路:
POJ2777 颜色数只有30 维护颜色状态S就能A
这题颜色数那么多。。怎么办?
修改:维护颜色替换标记col:区间颜色(多个颜色则col为0),满足区间加法
显然叶子col不为0
为什么维护这个呢,我的理解是:
如果没有col那么就需要
暴力查询,修改到每个叶子。修改复杂度是nlogn
现在有了col之后。
修改:如果当前区间是修改子区间我就不用往下修改了
查询:如果我发现当前区间col不为0,那么我就不用往下搜了
对吧,从而减少复杂度
(这里查询只有一次,暴力到叶子也没关系)
(主要还是修改不用一定到叶子减了很多复杂度w)
查询:【1,1e7】之内搜
用vis【】防止统计颜色时重复计算
当然:这题如果在1e7之内搜会超时
这里有个常规套路叫离散化
即颜色段可以压缩,如下:
段1:1 10 段2:1 3 段3:6 10 显然答案是3段
1 1 1 1 1 1 1 1 1 1
2 2 2 3 3 3 3 3
离散化: 1 10 1 3 6 10->1 3 6 10
即rk[1]=1,rk[2]=3,rk[3]=6,rk[4]=10
原来的3对应现在的2 原来的10对应现在的4....
所以离散化后现在变成下面了
段1:1 4 段2:1 2 段3:3 4
1 1 1 1
2 2 3 3
答案不对!怎么少了一段????
4 5被离散化合并掉了QAQ
这里只要我们对原先相邻大于1的插入1个数就好了
1 10 1 3 6 10->1 2 3 4 6 7 10
现在等价于
1 1 1 1 1 1 1
2 2 2 3 3 3
【1,3】颜色2 【3,5】颜色1 【5,7】颜色3
耶!成功了=w=
离散化之后rk[1]=1 rk[3]=3 rk[5]=6,rk[7]=10
非重复有序数列,对于某一个数想找下标,二分即可
难度:0.9
91ms
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
const int inf=0x3f3f3f;
struct point{
int li;
int ri;
};
bool vis[8*10005];
int rk[8*10005];
struct Stree
{
int col;
} sts[16*10005]; //开4倍
point ps[10005];
void pushup(int root)
{
if(sts[root<<1].col==sts[root<<1|1].col)
sts[root].col=sts[root<<1].col;
}
void pushdown(int root)
{
if(sts[root].col!=0)
{
sts[root<<1].col=sts[root].col;
sts[root<<1|1].col=sts[root].col;
sts[root].col=0;
}
}
void build(int l,int r,int root)//根节点从1开始吧
{
sts[root].col=0;//没了这句会WA,清空lazy跟val
if(l==r)//到达子节点
{
return;
}
else
{
int mid=(l+r)>>1;//(l+r)/2
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
// pushup(root);//更新本节点
}
}
int query(int nowl,int nowr,int root)
{
int ans=0;
if(sts[root].col)
{
if(!vis[sts[root].col])
{
vis[sts[root].col]=1;
return 1;
}
else return 0;
}
if(nowl==nowr)return 0;
pushdown(root);
int mid=(nowl+nowr)>>1;
ans+=query(nowl,mid,root<<1);
ans+=query(mid+1,nowr,root<<1|1);
return ans;
}
void update(int nowl,int nowr,int ul,int ur,int root,int addval)//nowl,nowr 当前区间 ul,ur 需修改的区间
{
if(ul<=nowl&&ur>=nowr)//当前区间是修改区间的子区间
{
sts[root].col=addval;
return ;
}
int mid=(nowl+nowr)>>1;
pushdown(root);//避难
if(ul<=mid) update(nowl,mid,ul,ur,root<<1,addval);//左子树与修改区间有交集
if(ur>mid) update(mid+1,nowr,ul,ur,root<<1|1,addval);
pushup(root);
return ;
}
void op();
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(vis,0,sizeof(vis));
int n;int m=0;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d %d",&ps[i].li,&ps[i].ri);
rk[++m]=ps[i].li;//离散化
rk[++m]=ps[i].ri;//离散化
}
// for(int i=1;i<=m;i++)
// printf("rkm:%d\n",rk[i]);
sort(rk+1,rk+m+1);//离散化 先排序
int tot=1;//去重,插入要重新定下标
for(int i=2;i<=m;i++)
if(rk[i]!=rk[i-1])rk[++tot]=rk[i];
for(int i=tot;i>=2;i--)//插入 当区间长度>=2要插入一个中间值
if(rk[i]!=rk[i-1]+1)rk[++tot]= rk[i]-1;
sort(rk+1,rk+tot+1);
// printf("tot:%d\n",tot);
build(1,tot,1);
// op();
for(int i=1;i<=n;i++)
{
int l=lower_bound(rk+1,rk+1+tot,ps[i].li)-rk;//找出第一个大于等于原下标的新下标
int r=lower_bound(rk+1,rk+1+tot,ps[i].ri)-rk;
update(1,tot,l,r,1,i);
//op();
}
// op();
int ans=query(1,tot,1);
printf("%d\n",ans);
}
}
void sp(int t)
{
for(int i=1;i<=t;i++)
printf(" ");
}
void op()
{
int pow2[10]={1,2,4,8,16,32};int n=16;
printf("\n///\n");
for(int i=1; i<=31; i++)
{
if(i==pow2[0])sp(15);
if(i==pow2[1])sp(7);
if(i==pow2[2])sp(3);
if(i==pow2[3])sp(1);
if(i>pow2[1]&&i<pow2[2])sp(15);
if(i>pow2[2]&&i<pow2[3])sp(7);
if(i>pow2[3]&&i<pow2[4])sp(3);
if(i>pow2[4]&&i<pow2[5])sp(1);
printf("%d",sts[i].col);
if((i==pow2[1]-1)||(i==pow2[2]-1)||(i==pow2[3]-1)||(i==pow2[4]-1))
printf("\n");
}
printf("\n///\n");
}
的