一、题目
二、题目链接
http://codeforces.com/contest/920/problem/F
三、题意
给定$N$个范围在$[1, 1e6)$的数字和$M$个操作。操作有两种类型:
$1$ $l$ $r$:更新区间$[l$, $r]$的数字ai为d[ai]。其中d[i]表示数字i的因子的个数。如:d[1] = 1, d[2] = 2, d[3] = 2, d[4] = 3。
$2$ $l$ $r$:查询区间$[l, r]$的数字和并输出。
四、思路
典型的数据结构题。很明显这是线段树的菜。
对于线段树的每个节点,维护所“管辖”区间的和值和最大值。
更新时,如果当前区间的最大值$maxv<=2$,那么,不需要往下更新下去了。因为$d[1] = 1$, $d[2] = 2$。否则,继续更新下去。这样并不会超时,因为对于一个范围在$[1, 1e6)$的数字而言,反复做$i = d[i]$这样的操作,次数并不会太多。
另外,更新完子区间后,要做上推合并操作。这是线段树很常见的操作。
五、源代码
#pragma GCC optimize(2) #pragma comment(linker, "/STACK:102400000, 102400000") #include<bits/stdc++.h> using namespace std; typedef long long LL; const int MAXN = 3e5 + 10; template <class T> inline void read(T &x) { int t; bool flag = false; while((t = getchar()) != '-' && (t < '0' || t > '9')) ; if(t == '-') flag = true, t = getchar(); x = t - '0'; while((t = getchar()) >= '0' && t <= '9') x = x * 10 + t - '0'; if(flag) x = -x; } typedef struct { LL maxv, sum; } Node; Node data[MAXN * 4]; int d[1 << 20], n, q; void init() { int N = 1000000; for(int i = 1; i <= N; ++i) { for(int j = i; j <= N; j += i) d[j]++; } memset(data, 0, sizeof(data)); } void pushup(int root){ data[root].sum = data[root << 1].sum + data[root << 1 | 1].sum; data[root].maxv = max(data[root << 1].maxv, data[root << 1 | 1].maxv); } void build(int x, int val, int root = 1, int l = 1, int r = n) { if(l > x || r < x)return; if(l == r && l == x) { data[root].sum = data[root].maxv = LL(val); return; } int mid = (l + r) >> 1; if(x <= mid)build(x, val, root << 1, l, mid); else build(x, val, root << 1 | 1, mid + 1, r); pushup(root); } void update(int ul, int ur, int root = 1, int l = 1, int r = n) { if(l > r || l > ur || r < ul)return; if(ul <= l && ur >= r && data[root].maxv <= 2LL)return; if(l == r) { data[root].sum = data[root].maxv = LL(d[data[root].sum]); return; } int mid = (l + r) >> 1; if(ul <= mid)update(ul, ur, root << 1, l, mid); if(ur > mid)update(ul, ur, root << 1 | 1, mid + 1, r); pushup(root); } LL query(int ql, int qr, int root = 1, int l = 1, int r = n) { if(l > r || l > qr || r < ql)return 0LL; if(ql <= l && qr >= r)return data[root].sum; int mid = (l + r) >> 1; return query(ql, qr, root << 1, l, mid) + query(ql, qr, root << 1 | 1, mid + 1, r); } int main() { #ifndef ONLINE_JUDGE freopen("Finput.txt", "r", stdin); #endif // ONLINE_JUDGE init(); int tmp, type, l, r; scanf("%d%d", &n, &q); for(int i = 1; i <= n; ++i){ read(tmp); build(i, tmp); } while(q--){ read(type), read(l), read(r); if(type == 1)update(l, r); else printf("%lld\n", query(l, r)); } return 0; }