hdu1698
lazy-tag(lazy标记)进行区间修改:
为了减小修改次数,先将要进行的修改存起来,当访问到子节点的时候,如果发现父节点带有标记,就顺便将子节点更新,就像搭顺风车一样。在这道题中,每次可能要将一个区间更改为一个值,比如全集为[1,7],现在要对区间[1,4]进行操作,例如要将[1,4]更改为1,如果采用单点更新,那么就要访问到叶节点一个一个第更新。实际上,当我们从[1,7]访问到它的左儿子[1,4]的时候,由于此时[1,4]恰好已经覆盖了要更新的区间,于是就将[1,4].sum = 1 * (4 - 1 + 1),当下次需要访问[1,4]的子节点时,发现[1,4]带有标记,就在访问[1,4]的子节点的时候顺便将以前记下的1操作,对[1,4]的子节点进行实施,同时将[1,4]的子节点带上标记。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
const int MAX = 444444;
struct node
{
int vis;//标记当前节点是否被更新
int sum;//当前区间的总价值
}a[MAX];
void Pushup(int x)
{
a[x].sum = a[x<<1].sum + a[x<<1|1].sum;
}
void build(int l,int r,int x)
{
a[x].vis = 0;//初始化时所有的节点都未被更新
a[x].sum = 1;
if(l==r)return ;
int mid = (l+r)>>1;
build(lson);
build(rson);
Pushup(x);
}
void Pushdown(int x,int m)//对当前节点的子节点进行更新
{
if(a[x].vis!=0)
{
a[x<<1].vis = a[x<<1|1].vis = a[x].vis;
a[x<<1].sum = (m - (m>>1)) * a[x].vis;
a[x<<1|1].sum = (m>>1) * a[x].vis;
a[x].vis = 0;
}
}
void update(int A,int B,int C,int l,int r,int x)//对线段树进行更新
{
if(A<=l && B>=r)//如果覆盖整个区间则将整段区间的值修改为C,区间价值即为C*(r - l + 1)
{
a[x].vis = C;
a[x].sum = C * (r - l + 1);
return ;
}
Pushdown(x,r - l + 1);//更新儿子节点
int mid = (l + r)>>1;
if(A<=mid) update(A,B,C,lson);//有覆盖到左儿子
if(B > mid) update(A,B,C,rson);//有覆盖到右儿子
Pushup(x);
}
int main()
{
int t,n,m,x,y,z,temp = 1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,1,n,1);
}
cout<<"Case "<<temp++<<": The total value of the hook is "<<a[1].sum<<"."<<endl;
}
return 0;
}
poj 3468
题意:给一些数,有两种操作。一种是某个区间都增加一个数,另一种是查询某段区间的和。
思路:在这道题中,每次可能要将一个区间更改为一个值,
比如全集为[1,7],现在要对区间[1,4]进行操作,
例如要将[1,4]之间的值都加上1,如果采用单点更新,
那么就要访问到叶节点一个一个第更新。
实际上,当我们从[1,7]访问到它的左儿子[1,4]的时候,
由于此时[1,4]恰好已经覆盖了要更新的区间,
于是就将[1,4].sum = 1 * (4 - 1 + 1),并将[1,4].vis += 1,
当下次需要访问[1,4]的子节点时,发现[1,4]带有标记,
就在访问[1,4]的子节点的时候顺便将以前记下的vis操作,
对[1,4]的子节点进行实施,同时将[1,4]的子节点带上标记。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
#define LL long long
const LL MAX = 444444;
struct node
{
LL vis;//标记是否进行过修改
LL sum;//当前区间的和
}a[MAX];
void Pushup(LL x)
{
a[x].sum = a[x<<1].sum + a[x<<1|1].sum;
}
void Pushdown(LL x,LL m)//更新当前节点并修改左右子节点
{
if(a[x].vis)
{
a[x<<1].vis += a[x].vis;
a[x<<1|1].vis += a[x].vis;//这里注意要用“+=”,因为有可能进行过多次修改,如果没累加的话,记录的只是访问之前的最后一次修改
a[x<<1].sum += (m - (m>>1)) * a[x].vis;
a[x<<1|1].sum += (m>>1) * a[x].vis;
a[x].vis = 0;
}
}
void build(LL l,LL r,LL x)
{
a[x].vis = 0;
a[x].sum = 0;
if(l==r) return ;
LL mid = (l+r)>>1;
build(lson);
build(rson);
}
void update(LL A,LL B,LL C,LL l,LL r,LL x)//对区间【A,B】进行更新
{
if(A<=l && B>=r)//完全覆盖区间【r,l】,返回区间的和即可
{
a[x].sum += (r - l + 1) * C;
a[x].vis += C;
return ;
}
Pushdown(x,r - l +1);//需要对节点x进行操作,所以要先修改x节点
LL mid = (l + r)>>1;
if(A<=mid) update(A,B,C,lson);//有覆盖到左区间
if(B > mid) update(A,B,C,rson);//有覆盖到右区间
Pushup(x);
}
LL query(LL A,LL B,LL l,LL r,LL x)//查找区间【A,B】的和
{
if(A<=l && B>=r) return a[x].sum;
Pushdown(x,r - l + 1);
LL mid = (l + r)>>1;
LL ret = 0;
if(A<=mid) ret += query(A,B,lson);
if(B > mid) ret += query(A,B,rson);
return ret;
}
int main()
{
LL n,q,i,k;
while(~scanf("%lld%lld",&n,&q))
{
build(1,n,1);
for(i = 1; i <= n; i++)
{
scanf("%lld",&k);
update(i,i,k,1,n,1);
}
char ch;
LL A,B,C;
while(q--)
{
getchar();
scanf("%c",&ch);
if(ch=='Q')
{
scanf("%lld%lld",&A,&B);
printf("%lld\n",query(A,B,1,n,1));
}
else if(ch=='C')
{
scanf("%lld%lld%lld",&A,&B,&C);
update(A,B,C,1,n,1);
}
}
}
return 0;
}
poj 2528
题意:在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报
(这题数据范围很大,直接搞超时+超内存,需要离散化:
离散化简单的来说就是只取我们需要的值来用,
比如说区间[1000,2000],[1990,2012]
我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,
所以我只需要1000,1990,2000,2012就够了,
将其分别映射到0,1,2,3,这样复杂度就大大的降下来了
所以离散化要保存所有需要用到的值,
排序后,分别映射到1~n,这样复杂度就会小很多很多)
思路:用到线段树,因为最多有10^6,
建那么大的树肯定会爆空间和时间,
所以想到要离散化,将每条线段的两端,
就是海报的两头分开考虑,所有的端点放在一起非递减排序,
去掉相同的,计算出有n个不同的点,编号从1到n,——这是离散化过程。
然后建二叉树。更新的时候先二分查找那条线段两端的点的编号,
然后在那棵建好的树上更新,遇到一样的节点就覆盖,
如果线段在那个节点的子节点上,而且这个节点又被其他线段覆盖过,
那么就要更新它的子节点,他自己改成不被覆盖的。
最后计算结果那里要用个辅助数组,从根节点开始找被覆盖的节点,
分别将不同的值放在数组里,最后看有几个结果就是几个。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
const int MAX = 11111;
int a[MAX<<4];
int ans;
bool hash[MAX<<4];
struct node1
{
int l,r;
}m[MAX];
void Pushdown(int x)
{
if(a[x]!=-1)//如果当前节点已被覆盖,则更新他的左右儿子节点,并将该节点改为没被覆盖
{
a[x<<1] = a[x];
a[x<<1|1] = a[x];
a[x] = -1;
}
}
void Update(int A,int B,int C,int l,int r,int x)
{
if(A<=l && B>=r)
{
a[x] = C;
return ;
}
Pushdown(x);
int mid = (l + r)>>1;
if(A<=mid) Update(A,B,C,lson);
if(B > mid) Update(A,B,C,rson);
}
void query(int l,int r,int x)
{
if(a[x]!=-1)
{
if(!hash[a[x]]) ans++;
hash[a[x]] = true;
return ;
}
if(l==r)return ;
int mid = (l + r)>>1;
query(lson);
query(rson);
}
int bin(int key,int n,int ma[])//离散化,用二分查找
{
int l = 0;
int r = n-1;
while(l<=r)
{
int mid = (l + r)>>1;
if(ma[mid]==key) return mid;
if(ma[mid] < key) l = mid + 1;
else r = mid - 1;
}
return -1;
}
int main()
{
int t,n,i;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int ma[MAX<<1];
int nu = 0;
for(i = 0; i < n; i++)
{
scanf("%d%d",&m[i].l,&m[i].r);
ma[nu++] = m[i].l;
ma[nu++] = m[i].r;
}
sort(ma,ma+nu);
int nu1 = 1;
for(i = 1; i < nu; i++)//去除重复的数
if(ma[i]!=ma[i-1]) ma[nu1++] = ma[i];
memset(a,-1,sizeof(a));
for(i = 0; i < n; i++)
{
int l = bin(m[i].l,nu1,ma);
int r = bin(m[i].r,nu1,ma);//l和r是离散化后所对应的值
Update(l,r,i,0,nu1-1,1);//更新区间【l,r】
}
ans = 0;
memset(hash,false,sizeof(hash));
query(0,nu1-1,1);
printf("%d\n",ans);
}
return 0;
}
poj 3225
题意:定义对集合的交、并、差、异或,求空集经过一系列操作后的结果。
题解:将原点集每一个点乘以2,形成一个新的点集,
其中偶数点都对应着原来的点,
奇数点对应着不包括它左右两个点的开区间,
即2k+1==>(k,k+1),
于是区间上所有点都能用整点表示,
线段树可求解,对于每一个操作区间,
无论开闭,都对应这线段树上的一段线段。
1、并运算[a,b],就是将[a,b]赋值为1.(a,b均为对应之后的点)
2、交运算[a,b],将除了[a,b]区间以外的线段清0.
3、S-[a,b],将[a,b]区间清0.
4、[a,b]-S,将[a,b]区间以外线段清0,并且将[a,b]线段所代表区域取反.
5、异或运算,上面两者去并,就是将[a,b]区域取反.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define lson l,mid,x<<1
#define rson mid+1,r,x<<1|1
const int MAX = 222222;
bool hash[MAX];
struct node
{
int Xor,cover;//用0和1表示是否包含区间,-1表示该区间内既有包含又有不包含
}a[MAX<<2];
void FXor(int x)
{
if(a[x].cover!=-1)
a[x].cover ^= 1;
else a[x].Xor ^= 1;
}
/*成段覆盖的操作很简单,比较特殊的就是区间0/1互换这个操作,
我们可以称之为异或操作。
很明显我们可以知道这个性质:
当一个区间被覆盖后,不管之前有没有异或标记都没有意义了。
所以当一个节点得到覆盖标记时把异或标记清空,
而当一个节点得到异或标记的时候,
先判断覆盖标记,如果是0或1,
直接改变一下覆盖标记,不然的话改变异或标记
*/
void Pushdown(int x)
{
if(a[x].cover!=-1)//有覆盖标记
{
a[x<<1].cover = a[x<<1|1].cover = a[x].cover;
a[x<<1].Xor = a[x<<1|1].Xor = 0;//清空标记
a[x].cover = -1;
}
if(a[x].Xor)//有异或标记
{
FXor(x<<1);
FXor(x<<1|1);
a[x].Xor = 0;
}
}
void Update(char ch,int A,int B,int l,int r,int x)
{
if(A<=l && B>=r)
{
if(ch=='U')
{
a[x].cover = 1;
a[x].Xor = 0;
}
else if(ch=='D')
{
a[x].cover = 0;
a[x].Xor = 0;
}
else if(ch=='C' || ch=='S')
{
FXor(x);
}
return ;
}
Pushdown(x);
int mid = (l + r)>>1;
if(A<=mid) Update(ch,A,B,lson);
else
{
if(ch=='I' || ch=='C')
{
a[x<<1].cover = a[x<<1].Xor = 0;
}
}
if(B > mid) Update(ch,A,B,rson);
else
{
if(ch=='I' || ch=='C')
{
a[x<<1|1].cover = a[x<<1|1].Xor = 0;
}
}
}
void query(int l,int r,int x)
{
if(a[x].cover==1)
{
for(int i = l; i <= r; i++)
hash[i] = true;
return ;
}
else if(a[x].cover==0)return ;
Pushdown(x);
int mid = (l + r)>>1;
query(lson);
query(rson);
}
int main()
{
a[1].cover = a[1].Xor = 0;
char ch,l,r;
int A,B;
while(~scanf("%c %c%d,%d%c\n",&ch,&l,&A,&B,&r))
{
/*离散化处理
线段树经常碰见的离散化问题。
对于这种经典的,要把常规思维中的点变成线的问题,
办法,就是将每个点倍增,这意味着,例如我们存储了{ 1, 2, 3, 4, 5}
代表了我们拥有的是(0,3),分开来讲,就是
(0, 1)、1、(1, 2)、2、(2, 3)。
*/
A<<=1;
B<<=1;
if(l=='(')A++;
if(r==')')B--;
/*还有一点需要注意,输入中的区间(a, b),由于a <= b,
可能出现类似(2, 2)这样的区间,应看成空。
*/
if(A > B)
{
if(ch=='I' || ch=='C')
a[1].cover = a[1].Xor = 0;
}
else Update(ch,A,B,0,MAX,1);
}
query(0,MAX,1);
bool flag = false;
int e,s = -1;
for(int i = 0; i <= MAX; i++)
{
if(hash[i]==1)
{
if(s==-1)
s = i;
e = i;
}
else
{
if(s!=-1)
{
if(flag) printf(" ");
flag = true;
if(s&1)
printf("(");
else printf("[");
printf("%d,%d",s>>1,(e+1)>>1);
if(e&1)
printf(")");
else printf("]");
s = -1;
}
}
}
if(!flag) printf("empty set");
puts("");
return 0;
}