首页 > 代码库 > BZOJ 3211 花神游历各国
BZOJ 3211 花神游历各国
题意:
给出N(<=1e5)个数,每个数字在[1, 1e9]这个范围,有m(<=2e5)次操作,分为两种,①将区间[L, R]所有数开平方,②询问区间[L, R]数字之和。
题解:
1.因为每个数字在1e9以内那么开平方到1,次数小于10的,所以想要每次开方跳过为1的数,现在就将操作改为单点修改,复杂度也满足。
2.现在的问题是单点修改,询问区间和了,树状数组就可以解决~\(≧▽≦)/~
3.但是怎么才能每次开方跳过1呢?并查集可以维护,如果一个数开方以后等于1了,就将它连向它后面那个不为1的数,实现删除操作,具体见代码~
代码:
/************************************************************** Problem: 3211 User: Xgtao Language: C++ Result: Accepted Time:1140 ms Memory:5908 kb ****************************************************************/ #include <cstdio> #include <cmath> #include <iostream> using namespace std; #define lowbit(i) i&-i #define ll long long const int N = 1e5 + 7; int pa[N], n, m, o, L, R, x[N]; ll S[N]; int readint () { int K = 0; char c = getchar (); while (c < ‘0‘ || c > ‘9‘) c = getchar (); while (c >= ‘0‘ && c <= ‘9‘) K = K * 10 + c - ‘0‘, c = getchar (); return K; } int find (int x) { return pa[x] == x ? x : pa[x] = find (pa[x]); } void update (int x, int w) { for (int i = x; i < N; i += lowbit (i)) S[i] += w; } ll sum (int x) { ll ret = 0; for (int i = x; i >= 1; i -= lowbit (i)) ret += S[i]; return ret; } int main () { n = readint(); for (int i = 1; i <= n + 1; ++i) pa[i] = i; for (int i = 1; i <= n; ++i) { x[i] = readint(); if (x[i] == 1 || x[i] == 0) pa[i] = find (i + 1); update (i, x[i]); } m = readint(); while (m--) { o = readint(), L = readint(), R = readint(); if (o == 1) printf ("%lld\n", sum (R) - sum (L - 1)); else if (o == 2) { for (int i = find (L); i <= R; i = find (i + 1)) { update (i, (int)sqrt (x[i]) - x[i]); x[i] = sqrt (x[i]); if (x[i] == 1) pa[i] = find (i + 1); } } } return 0; }
总结:
并查集的姿势很重要,链表式的套路要好好掌握啊~
BZOJ 3211 花神游历各国
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。