BZOJ3211 花神游历各国(分块 区间开根号)
题意:给n个数,可以进行两种操作:给区间[l,r]每个数开方向下取整;算区间[l,r]的和。
思路:我们可以知道,一个数一直开方下去,就会变成0或者1,然后就不会变了。那么当一个区间只剩0或1时,就不用进行操作了。那么直接分块,然后搞一个flag判断一下是否变成0。稍微优化一下。
代码:
#include<cmath> #include<set> #include<map> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 1e5 + 10; const int M = maxn * 30; const ull seed = 131; const int INF = 0x3f3f3f3f; const int MOD = 1e9 + 7; int belong[maxn], block; int flag[maxn]; ll sum[maxn]; ll a[maxn]; struct Block{ int l, r; }b[maxn]; void change(int l, int r){ int bl = belong[l], br = belong[r]; if(bl == br){ if(!flag[bl]){ for(int i = l; i <= r; i++){ sum[bl] -= a[i]; a[i] = (int)sqrt(a[i]); sum[bl] += a[i]; } flag[bl] = 1; for(int j = b[bl].l; j <= b[bl].r; j++){ if(a[j] > 1){ flag[bl] = 0; break; } } } } else{ if(!flag[bl]){ for(int i = l; i <= b[bl].r; i++){ sum[bl] -= a[i]; a[i] = (int)sqrt(a[i]); sum[bl] += a[i]; } flag[bl] = 1; for(int j = b[bl].l; j <= b[bl].r; j++){ if(a[j] > 1){ flag[bl] = 0; break; } } } for(int i = bl + 1; i <= br - 1; i++){ if(!flag[i]){ sum[i] = 0; flag[i] = 1; for(int j = b[i].l; j <= b[i].r; j++){ a[j] = (int)sqrt(a[j]); sum[i] += a[j]; if(a[j] > 1) flag[i] = 0; } } } if(!flag[br]){ for(int i = b[br].l; i <= r; i++){ sum[br] -= a[i]; a[i] = (int)sqrt(a[i]); sum[br] += a[i]; } flag[br] = 1; for(int j = b[br].l; j <= b[br].r; j++){ if(a[j] > 1){ flag[br] = 0; break; } } } } } ll query(int l, int r){ int bl = belong[l], br = belong[r]; ll ans = 0; if(bl == br){ for(int i = l; i <= r; i++){ ans += a[i]; } } else{ for(int i = l; i <= b[bl].r; i++){ ans += a[i]; } for(int i = bl + 1; i <= br - 1; i++){ ans += sum[i]; } for(int i = b[br].l; i <= r; i++){ ans += a[i]; } } return ans; } int main(){ int n; scanf("%d", &n); for(int i = 1; i <= n; i++){ scanf("%lld", &a[i]); } block = sqrt(n); for(int i = 1; i <= n; i++){ belong[i] = (i - 1) / block + 1; } for(int i = 1; i <= belong[n]; i++){ b[i].l = (i - 1) * block + 1; b[i].r = min(n, b[i].l + block - 1); sum[i] = flag[i] = 0; for(int j = b[i].l; j <= b[i].r; j++) sum[i] += a[j]; } int m; scanf("%d", &m); for(int i = 1; i <= m; i++){ int o, l, r; int c; scanf("%d%d%d", &o, &l, &r); if(o == 2){ change(l, r); } else{ printf("%lld\n", query(l, r)); } } return 0; }