题意:给一个数字序列,第一类操作是将[l,r]内的数全赋为x ,第二类操作是将[l,r]中大于x的数赋为该数与x的gcd,若干操作后输出整个序列。
解法: 本题线段树要维护的最重要的东西就是一个区间内所有数是否相等的标记。只维护这个东西都可以做出来。 我当时想歪了,想到维护Max[rt]表示该段的最大值,最大值如果<=x的话就不用更新了,但是好像加了这个“优化”跑的更慢了。
我想大概是因为如果两个子树最大值或者整个两个子树的数不完全相等的话,根本不能直接下传这个值或者下传gcd,因为你不知道要更新哪个值。所以要维护一个区间是否相等的标记,而且这样最坏情况下也是更新到叶子节点,正好使我们要更新的。
只要弄懂了要维护什么,其余的就不难了。
代码1:(加一个Max标记)
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> using namespace std; #define N 100007 int Max[4*N],mark[4*N]; //mark[rt]=-1表示此段值不统一,否则mark[rt]会等于该段的统一值 int gcd(int a,int b) { if(!b) return a; return gcd(b,a%b); } void pushup(int rt) { Max[rt] = max(Max[2*rt],Max[2*rt+1]); } void pushdown(int l,int r,int rt) { if(mark[rt] != -1) //此段值相同 { mark[2*rt] = mark[2*rt+1] = mark[rt]; Max[2*rt] = Max[2*rt+1] = Max[rt]; mark[rt] = -1; } } void build(int l,int r,int rt) { mark[rt] = -1; if(l == r) { scanf("%d",&Max[rt]); mark[rt] = Max[rt]; return; } int mid = (l+r)/2; build(l,mid,2*rt); build(mid+1,r,2*rt+1); pushup(rt); } void update(int l,int r,int aa,int bb,int val,int tag,int rt) { if(aa <= l && bb >= r) { if(tag == 1) { Max[rt] = mark[rt] = val; return; } else { if(mark[rt] != -1) //此段值相等 { if(Max[rt] > val) //要更新 Max[rt] = mark[rt] = gcd(mark[rt],val); return; } } } pushdown(l,r,rt); int mid = (l+r)/2; if(aa <= mid) update(l,mid,aa,bb,val,tag,2*rt); if(bb > mid) update(mid+1,r,aa,bb,val,tag,2*rt+1); pushup(rt); } void print(int l,int r,int rt) { if(l == r) { printf("%d ",Max[rt]); return; } pushdown(l,r,rt); int mid = (l+r)/2; print(l,mid,2*rt); print(mid+1,r,2*rt+1); } int main() { int n,m,i,t,tag,aa,bb,val; scanf("%d",&t); while(t--) { scanf("%d",&n); build(1,n,1); scanf("%d",&m); while(m--) { scanf("%d%d%d%d",&tag,&aa,&bb,&val); update(1,n,aa,bb,val,tag,1); } print(1,n,1); puts(""); } return 0; }
代码2:(只维护一个)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100007
int Max[4*N];
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
void pushup(int rt)
{
if(Max[2*rt] == Max[2*rt+1]) Max[rt] = Max[2*rt];
}
void pushdown(int l,int r,int rt)
{
if(Max[rt] != -1)
Max[2*rt] = Max[2*rt+1] = Max[rt], Max[rt] = -1;
}
void build(int l,int r,int rt)
{
Max[rt] = -1;
if(l == r)
{
scanf("%d",&Max[rt]);
return;
}
int mid = (l+r)/2;
build(l,mid,2*rt);
build(mid+1,r,2*rt+1);
pushup(rt);
}
void update(int l,int r,int aa,int bb,int val,int tag,int rt)
{
if(aa <= l && bb >= r)
{
if(tag == 1)
{
Max[rt] = val;
return;
}
else
{
if(Max[rt] != -1)
{
if(Max[rt] > val) //下面有比它大的,即有要更新的
Max[rt] = gcd(Max[rt],val);
return;
}
}
}
pushdown(l,r,rt);
int mid = (l+r)/2;
if(aa <= mid) update(l,mid,aa,bb,val,tag,2*rt);
if(bb > mid) update(mid+1,r,aa,bb,val,tag,2*rt+1);
pushup(rt);
}
void print(int l,int r,int rt)
{
if(l == r)
{
printf("%d ",Max[rt]);
return;
}
pushdown(l,r,rt);
int mid = (l+r)/2;
print(l,mid,2*rt);
print(mid+1,r,2*rt+1);
}
int main()
{
int n,m,i,t,tag,aa,bb,val;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
build(1,n,1);
scanf("%d",&m);
while(m--)
{
scanf("%d%d%d%d",&tag,&aa,&bb,&val);
update(1,n,aa,bb,val,tag,1);
}
print(1,n,1);
puts("");
}
return 0;
}