Loj#6281. 数列分块入门 5
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间开方,区间求和。
样例输入
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
样例输出
6
2
分析:这道题没办法维护整块的信息,必须要知道每一个元素具体是多少才行.
开方的一个性质:2 ^ 32内的数最多经过6次开方就变成了0或1,也就是说一个数最多只会被修改6次. 分块维护当前区间是不是全是0/1,是的话就跳过修改这个区间.
数组开小导致一直WA......
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll maxn = 50010; ll n,a[maxn],sum[maxn],pos[maxn],block,L[maxn],R[maxn],cnt; bool flag[maxn]; void modify(ll x) { if (flag[x]) return; flag[x] = 1; sum[x] = 0; for (ll i = L[x]; i <= R[x]; i++) { a[i] = sqrt(a[i]); sum[x] += a[i]; if (a[i] > 1) flag[x] = 0; } } void update(ll l,ll r) { if (pos[l] == pos[r]) { for (ll i = l; i <= r; i++) { sum[pos[l]] -= a[i]; a[i] = sqrt(a[i]); sum[pos[l]] += a[i]; } return; } for (ll i = l; i <= R[pos[l]]; i++) { sum[pos[l]] -= a[i]; a[i] = sqrt(a[i]); sum[pos[l]] += a[i]; } for (ll i = L[pos[r]]; i <= r; i++) { sum[pos[r]] -= a[i]; a[i] = sqrt(a[i]); sum[pos[r]] += a[i]; } for (ll i = pos[l] + 1; i <= pos[r] - 1; i++) modify(i); } ll query(ll l,ll r) { ll res = 0; if (pos[l] == pos[r]) { for (ll i = l; i <= r; i++) res += a[i]; return res; } for (ll i = l; i <= R[pos[l]]; i++) res += a[i]; for (ll i = L[pos[r]]; i <= r; i++) res += a[i]; for (ll i = pos[l] + 1; i <= pos[r] - 1; i++) res += sum[i]; return res; } int main() { scanf("%lld",&n); block = sqrt(n); for (ll i = 1; i <= n; i++) { scanf("%lld",&a[i]); pos[i] = (i - 1) / block + 1; } cnt = (n - 1) / block + 1; for (ll i = 1; i <= cnt; i++) { L[i] = R[i - 1] + 1; R[i] = min(n,block * i); for (ll j = L[i]; j <= R[i]; j++) sum[i] += a[j]; } for (ll i = 1; i <= n; i++) { ll opt,l,r,c; scanf("%lld%lld%lld%lld",&opt,&l,&r,&c); if (opt == 0) update(l,r); else printf("%lld\n",query(l,r)); } return 0; }