Problem1
题意:
一个长度为 n n n的数组 a a a,操作1:求区间 [ l , r ] [l,r] [l,r]的和,操作2:将区间 [ l , r ] [l,r] [l,r]的 a [ i ] a[i] a[i]变为 a [ i ] − l o w b i t ( a [ i ] ) a[i]-lowbit(a[i]) a[i]−lowbit(a[i]),操作3:将区间 [ l , r ] [l,r] [l,r]的 a [ i ] a[i] a[i]变为 a [ i ] + 2 k ,其中 k 满足 2 k ≤ a [ i ] < 2 k + 1 a[i]+2^k,其中k满足2^k\leq a[i]< 2^{k+1} a[i]+2k,其中k满足2k≤a[i]<2k+1,
思路:
我们发现两种操作不会改变数在二进制下的1的个数,且操作2会每次减少一个1。那么如果某个区间的所有数的二进制1的个数为0,那么不再修改,结束递归,否则就一直递归进行单点修改。对于操作3,则相对于二进制下的区间乘。
代码:
const int N = 1e5 + 50;
const int M = N << 1;
const int mod = 998244353;
#define int LL
struct Node {
int l, r;
int high, low, lazy, cnt;
}tr[N << 2];
int n, a[N], Left[N * 2];
void pushup(int u) {
tr[u].high = (tr[lson].high + tr[rson].high) % mod;
tr[u].low = (tr[lson].low + tr[rson].low) % mod;
tr[u].cnt = max(tr[lson].cnt, tr[rson].cnt);
}
void pushdown(int u) {
if(tr[u].lazy) {
tr[lson].high = tr[lson].high * Left[tr[u].lazy] % mod;
tr[rson].high = tr[rson].high * Left[tr[u].lazy] % mod;
tr[lson].lazy += tr[u].lazy;
tr[rson].lazy += tr[u].lazy;
tr[u].lazy = 0;
}
}
int get(int x) {
for(int i = 30; i >= 0; i--) {
if(x >> i & 1) {
return 1 << i;
}
}
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 0, 0, 0};
if(l == r) {
tr[u].high = get(a[l]);
tr[u].low = a[l] - tr[u].high;
tr[u].cnt = __builtin_popcount(a[l]);
return;
}
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(u);
}
void modify1(int u, int l, int r) {
if(!tr[u].cnt) return;
if(tr[u].l == tr[u].r) {
if(tr[u].cnt > 1) {
tr[u].low -= lowbit(tr[u].low);
tr[u].cnt--;
} else {
tr[u].high = tr[u].low = tr[u].cnt = 0;
}
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify1(lson, l, r);
if(r > mid) modify1(rson, l, r);
pushup(u);
}
void modify2(int u, int l, int r) {
if(l <= tr[u].l && tr[u].r <= r) {
tr[u].high *= 2;
tr[u].high %= mod;
tr[u].lazy++;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify2(lson, l, r);
if(r > mid) modify2(rson, l, r);
pushup(u);
}
int query(int u, int l, int r) {
if(!tr[u].cnt) return 0;
if(l <= tr[u].l && tr[u].r <= r) {
return tr[u].high + tr[u].low;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if(l <= mid) {
res += query(lson, l, r);
res %= mod;
}
if(r > mid) {
res += query(rson, l, r);
res %= mod;
}
return res;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
Left[1] = 2;
for(int i = 2; i < N * 2 - 10; i++) {
Left[i] = Left[i - 1] * 2 % mod;
}
int T;
cin >> T;
while(T--) {
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
int q; cin >> q;
while(q--) {
int op, l, r; cin >> op >> l >> r;
if(op == 1) {
cout << query(1, l, r) << '\n';
} else if(op == 2) {
modify1(1, l, r);
} else {
modify2(1, l, r);
}
}
}
return 0;
}
Problem2
题意:
给定一个长度为 n n n的序列 a , 0 < a [ i ] ≤ 100 a,0< a[i]\leq100 a,0<a[i]≤100。操作0:将区间 [ l , r ] [l,r] [l,r]都乘 w , 0 < w ≤ 100 w,0<w\leq 100 w,0<w≤100,操作1:求区间 [ l , r ] [l,r] [l,r]的数的欧拉函数值的和。
思路:
首先有以下结论(
p
p
p是质数):
i
%
p
=
0
:
ψ
(
i
∗
p
)
=
ψ
(
i
)
∗
p
i\%p=0:\psi(i*p)=\psi(i)*p
i%p=0:ψ(i∗p)=ψ(i)∗p
i
%
p
≠
0
:
ψ
(
i
∗
p
)
=
ψ
(
i
)
∗
(
p
−
1
)
i\%p \neq 0:\psi(i*p)=\psi(i)*(p-1)
i%p=0:ψ(i∗p)=ψ(i)∗(p−1)
观察数据范围,100以内的质数不到30个,那么我们可以将
w
w
w拆分成质数进行区间乘的修改。
那么如果对于某个区间全部含有因子质数
p
p
p,那么直接进行区间乘,否则继续递归修改。细节较多。
代码:
#define int LL
struct Node {
int val, lazy;
bitset<30> f;
}tr[N << 2];
int n, a[N];
int cnt[111][35];
int pri[111], vis[111], ouler[111];
bitset<30>st[111];
void pushup(int u) {
tr[u].val = (tr[lson].val + tr[rson].val) % mod;
tr[u].f = tr[lson].f & tr[rson].f;
}
void pushdown(int u) {
if(tr[u].lazy == 1) return;
assert(tr[u].lazy != 0);
tr[lson].val = 1LL * tr[lson].val * tr[u].lazy % mod;
tr[rson].val = 1LL * tr[rson].val * tr[u].lazy % mod;
tr[lson].lazy = 1LL * tr[lson].lazy * tr[u].lazy % mod;
tr[rson].lazy = 1LL * tr[rson].lazy * tr[u].lazy % mod;
tr[u].lazy = 1;
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {ouler[a[l]], 1, st[a[l]]};
return;
}
tr[u].lazy = 1;
int mid = l + r >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int L, int R, int p1, int p2) {
if(tr[u].f[p2] && L <= l && r <= R) {
tr[u].lazy = 1LL * tr[u].lazy * ksm(pri[p2], p1) % mod;
tr[u].val = 1LL * tr[u].val * ksm(pri[p2], p1) % mod;
return;
}
if(l == r) {
if(tr[u].f[p2])
tr[u].val = (tr[u].val + ksm(pri[p2], p1)) % mod;
else {
tr[u].f[p2] = 1;
tr[u].val = 1LL * tr[u].val * (pri[p2] - 1) % mod;
tr[u].val = 1LL * tr[u].val * ksm(pri[p2], p1 - 1) % mod;
}
return;
}
pushdown(u);
int mid = l + r >> 1;
if(L <= mid) modify(lson, l, mid, L, R, p1, p2);
if(R > mid) modify(rson, mid + 1, r, L, R, p1, p2);
pushup(u);
}
int query(int u, int l, int r, int L, int R) {
if(L <= l && r <= R) return tr[u].val;
pushdown(u);
int mid = l + r >> 1;
int res = 0;
if(L <= mid) {
res += query(lson, l, mid, L, R);
res %= mod;
}
if(R > mid) {
res += query(rson, mid + 1, r, L, R);
res %= mod;
}
return res;
}
LL ans[N];
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
ouler[1] = 1;
for(int i = 2; i <= 100; i++) {
if(!vis[i]) {
pri[++pri[0]] = i;
ouler[i] = i - 1;
}
for(int j = 1; j <= pri[0]; j++) {
assert(pri[j] != 0);
if(i * pri[j] > 100) break;
vis[i * pri[j]] = 1;
if(i % pri[j] == 0) {
ouler[i * pri[j]] = ouler[i] * pri[j];
break;
} else {
ouler[i * pri[j]] = ouler[i] * ouler[pri[j]];
}
}
}
for(int i = 2; i <= 100; i++) {
for(int j = 1; j <= pri[0]; j++) {
int x = i;
while(x % pri[j] == 0) {
assert(pri[j] != 0);
cnt[i][j]++;
x /= pri[j];
}
st[i][j] = cnt[i][j];
}
}
// D(pri[0])
int m;
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
while(m--) {
int op, l, r; cin >> op >> l >> r;
if(op == 1) {
ans[++ans[0]] = query(1, 1, n, l, r);
} else {
int x; cin >> x;
for(int j = 1; j <= pri[0]; j++) {
if(st[x][j]) {
modify(1, 1, n, l, r, cnt[x][j], j);
}
}
}
}
for (int i = 1; i < ans[0]; i++) {
cout << ans[i] << '\n';
}
if (ans[0]) {
cout << ans[ans[0]];
}
return 0;
}