题意:
给你一个区间,每次可以进行两种操作
1:把区间中的数全都变成x
2:把区间中大于x的数变成gcd(a[i], x)
最后输出序列。(n,m<=10^6)
时限 15s,暴力也能过。。。。
线段树做法,区间更新,lazy标记,到需要更新的时候才更新。
num[rt] != -1,表示 区间 L[rt]~R[rt] 所有的数都相同。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
int num[maxn*4], lazy[maxn*4], L[maxn*4], R[maxn*4];
int gcd(int a, int b){ return b?gcd(b, a%b):a; }
void PushUp(int rt){
if(num[rt<<1]==num[rt<<1|1]){
num[rt] = num[rt<<1];
}else num[rt] = -1;
}
void PushDown(int rt)
{
if(lazy[rt]){
lazy[rt<<1] = lazy[rt<<1|1] = 1;
num[rt<<1] = num[rt<<1|1] = num[rt];
lazy[rt] = 0;
}
}
void build(int rt, int l, int r)
{
L[rt] = l, R[rt] = r;
lazy[rt] = 0;
if(l == r)
{
scanf("%d", &num[rt]);
return ;
}
int mid = (l+r)>>1;
build(rt<<1, l, mid);
build(rt<<1|1, mid+1, r);
}
void update1(int rt, int l, int r, int x){
if(l<=L[rt] && R[rt]<=r){
lazy[rt] = 1;
num[rt] = x;
return ;
}
PushDown(rt);
int mid = (L[rt]+R[rt])>>1;
if(l<=mid) update1(rt<<1, l, r, x);
if(r> mid) update1(rt<<1|1, l, r, x);
PushUp(rt);
}
void update2(int rt, int l, int r, int x)
{
if(l<=L[rt] && R[rt]<=r && num[rt]!=-1) {
if(num[rt]>x){
num[rt] = gcd(num[rt], x);
lazy[rt] = 1;
}
return ;
}
PushDown(rt);
int mid = (L[rt]+R[rt])>>1;
if(l<=mid) update2(rt<<1, l, r, x);
if(r> mid) update2(rt<<1|1, l, r, x);
PushUp(rt);
}
void query(int rt)
{
if(L[rt]==R[rt]){
printf("%d ", num[rt]);
return ;
}
PushDown(rt);
int mid = (L[rt]+R[rt])>>1;
query(rt<<1);
query(rt<<1|1);
}
int main()
{
int n, m, i, T, t, l, r, x;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
build(1, 1, n);
scanf("%d", &m);
while(m--)
{
scanf("%d%d%d%d", &t, &l, &r, &x);
if(t==1){
update1(1, l, r, x);
}else {
update2(1, l, r, x);
}
}
query(1);
printf("\n");
}
return 0;
}