Atcoder abc256

https://atcoder.jp/contests/abc256/tasks

Ex - I like Query Problem

长n序列A

q个操作

类型1, a[l..r] 整除 x

类型2, a[l..r] = x

类型3, 求 sum a[l..r]

范围

8s

1024mb

n 5e5

q 1e5

ai [1,1e5]

x [2,1e5]

我的思路

感觉就线段树啊

(a/x) = (a/(x * y)) 吧

所以记录一下区间 的操作,

问题是求和的话??

另一个思路就是, 记录连续一样的段, 这样每次操作到连续一样的段上, 如果本来就是一致的就直接除就行了, 而不一致的,每个位置的值在重新赋值前最多除log(2,a[i]) 次

似乎就被均摊了?

然后向上搜集的时候, 注意把原来不一样但除了以后一样的也做合并

似乎就没了?

代码

https://atcoder.jp/contests/abc256/submissions/35522647

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <bits/stdc++.h>
typedef long long ll;
#define rep(i,a,n) for (ll i=a;i<(ll)n;i++)

#define SEG_ROOT 1,0,n-1
#define SEG_L (o<<1)
#define SEG_R (o<<1|1)
#define mid (l+r)/2
#define SEG_L_CHILD SEG_L,l,mid
#define SEG_R_CHILD SEG_R,mid+1,r

ll read(){ll r;scanf("%lld",&r);return r;}

// Ex
using A3=std::array<ll,3>;
A3 seg[2'000'010]; // {sum, same?, value(只在same==true时才有意义)}

ll a[500'010];

void down(int o,int l,int r){
assert(l!=r);
if(seg[o][1]){
ll v=seg[o][2];
seg[SEG_L]={(mid-l+1)*v,true,v};
seg[SEG_R]={(r-mid)*v,true,v};
}
}

A3 up(int o){
auto [s0,b0,v0]=seg[SEG_L];
auto [s1,b1,v1]=seg[SEG_R];
return seg[o]={s0+s1,b0&&b1&&v0==v1,v0};
}


A3 build(int o,int l,int r){
if(l==r)return seg[o]={a[l],true,a[l]};
build(SEG_L_CHILD);
build(SEG_R_CHILD);
return up(o);
}

A3 div(int o,int l,int r,int ql,int qr,ll x){
if(ql<=l&&r<=qr&&seg[o][1]){
seg[o][2]/=x;
seg[o][0]=(r-l+1)*seg[o][2];
return seg[o];
}
down(o,l,r);
if(ql<=mid) div(SEG_L_CHILD,ql,qr,x);
if(qr> mid) div(SEG_R_CHILD,ql,qr,x);
return up(o);
}

A3 set(int o,int l,int r,int ql,int qr,ll x){
if(ql<=l&&r<=qr) return seg[o]={x*(r-l+1),true,x};
down(o,l,r);
if(ql<=mid) set(SEG_L_CHILD,ql,qr,x);
if(qr> mid) set(SEG_R_CHILD,ql,qr,x);
return up(o);
}

ll sum(int o,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return seg[o][0];
down(o,l,r);
ll ret=0;
if(ql<=mid)ret+=sum(SEG_L_CHILD,ql,qr);
if(qr> mid)ret+=sum(SEG_R_CHILD,ql,qr);
return ret;
}

int main(){
int n=read();
int q=read();
rep(i,0,n)a[i]=read();
build(SEG_ROOT);
while(q--){
int op=read();
int l=read()-1;
int r=read()-1;
if(op==1){
div(SEG_ROOT,l,r,read());
}else if(op==2){
set(SEG_ROOT,l,r,read());
}else{
printf("%lld\n",sum(SEG_ROOT,l,r));
}
}
return 0;
}

总结

Ex

没啥难的, 注意到收集过程 和 均摊代价

这橙色是因为放在最后一题大家没时间做吧, 感觉也就个黄色题目差不多了

参考