P10009 [集训队互测 2022] 线段树 题解
题目链接:P10009 [集训队互测 2022] 线段树
神仙分块题,先给一下出题人的神仙官解:
官解链接
前面还看得懂。后面是啥?这不是 ds 题咋和 dp、轮廓线扯上关系了。看了半天,还是这个启发了我:
其手玩下,在 Excel 里写一下,可以理解到这里其实是想表达的一个核心意思是啥:对于一组序列而言,我们对操作
证明可以参照 pdf 里面说的,转化为网格图,然后转变为路径数问题,考虑一个点是否计入最终点的贡献。因为异或两次就为
这点还是很好理解的。而
很显然的是由卢卡斯定理我们有:
显然需要有每个
接下来只需要证明
最初始的
非 次操作该如何解决。答案是倍增/二进制分解,把它分解为若干个 次操作累计就行了。这样一来我们就解决了“整块操作”。散块暴力即可。结束。
算法框架及其细节
首先,需要注意一点,既然都上分块了,那么显而易见的
其次,对于查询而言,我们可以遍历每个查询和每个块,记录整块操作数,遇到散块就直接处理完整块的就行了。这一部分外层遍历的复杂度为
所以单次查询的最坏复杂度
而又因为这
又因为最多有
参考代码
#include <bits/stdc++.h> //#pragma GCC optimize("Ofast,unroll-loops") #define isPbdsFile #ifdef isPbdsFile #include <bits/extc++.h> #else #include <ext/pb_ds/priority_queue.hpp> #include <ext/pb_ds/hash_policy.hpp> #include <ext/pb_ds/tree_policy.hpp> #include <ext/pb_ds/trie_policy.hpp> #include <ext/pb_ds/tag_and_trait.hpp> #include <ext/pb_ds/hash_policy.hpp> #include <ext/pb_ds/list_update_policy.hpp> #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/exception.hpp> #include <ext/rope> #endif using namespace std; using namespace __gnu_cxx; using namespace __gnu_pbds; typedef long long ll; typedef long double ld; typedef pair<int, int> pii; typedef pair<ll, ll> pll; typedef tuple<int, int, int> tii; typedef tuple<ll, ll, ll> tll; typedef unsigned int ui; typedef unsigned long long ull; typedef __int128 i128; #define hash1 unordered_map #define hash2 gp_hash_table #define hash3 cc_hash_table #define stdHeap std::priority_queue #define pbdsHeap __gnu_pbds::priority_queue #define sortArr(a, n) sort(a+1,a+n+1) #define all(v) v.begin(),v.end() #define yes cout<<"YES" #define no cout<<"NO" #define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr); #define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout); #define forn(i, a, b) for(int i = a; i <= b; i++) #define forv(i, a, b) for(int i=a;i>=b;i--) #define ls(x) (x<<1) #define rs(x) (x<<1|1) #define endl '\n' //用于Miller-Rabin [[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; template <typename T> int disc(T* a, int n) { return unique(a + 1, a + n + 1) - (a + 1); } template <typename T> T lowBit(T x) { return x & -x; } template <typename T> T Rand(T l, T r) { static mt19937 Rand(time(nullptr)); uniform_int_distribution<T> dis(l, r); return dis(Rand); } template <typename T1, typename T2> T1 modt(T1 a, T2 b) { return (a % b + b) % b; } template <typename T1, typename T2, typename T3> T1 qPow(T1 a, T2 b, T3 c) { a %= c; T1 ans = 1; for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c; return modt(ans, c); } template <typename T> void read(T& x) { x = 0; T sign = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-')sign = -1; ch = getchar(); } while (isdigit(ch)) { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } x *= sign; } template <typename T, typename... U> void read(T& x, U&... y) { read(x); read(y...); } template <typename T> void write(T x) { if (typeid(x) == typeid(char))return; if (x < 0)x = -x, putchar('-'); if (x > 9)write(x / 10); putchar(x % 10 ^ 48); } template <typename C, typename T, typename... U> void write(C c, T x, U... y) { write(x), putchar(c); write(c, y...); } template <typename T11, typename T22, typename T33> struct T3 { T11 one; T22 tow; T33 three; bool operator<(const T3 other) const { if (one == other.one) { if (tow == other.tow)return three < other.three; return tow < other.tow; } return one < other.one; } T3() { one = tow = three = 0; } T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three) { } }; template <typename T1, typename T2> void uMax(T1& x, T2 y) { if (x < y)x = y; } template <typename T1, typename T2> void uMin(T1& x, T2 y) { if (x > y)x = y; } constexpr int N = 2.5e5 + 10; constexpr int SIZE = sqrt(N); constexpr int CNT = (N + SIZE - 1) / SIZE + 1; int pos[N]; int s[CNT], e[CNT]; int pre[N]; //上一轮序列 int nxt[N]; //下一轮序列 int tmp[N]; //临时序列 int ans[N]; //答案 int n, q; int siz, cnt; //块大小,块数量 struct Query { int op, l, r, id; } qu[N]; //整块更新,2^i次方就直接tmp[x]^=tmp[x-2^i],否则二进制拆分倍增 inline void allUpdate(const int id, int updateCnt) { while (updateCnt) { int t = log2(updateCnt); int step = 1 << t; //步长 //两块两块处理,因为updateCnt最多为sqrt(n) forv(i, e[id], s[id-1]+step)tmp[i] ^= tmp[i - step]; updateCnt -= step; } } //处理操作 inline void update(const int queryCnt) { //遍历每个块进行贡献更新 forn(idx, 1, cnt) { int blockCnt = 0; //整块操作次数 //两块两块进行处理,对于当前块,同时拿到它之前的块进行一并处理方便a[i]^=a[i-step],step=sqrt(n)。 forn(i, s[idx-1], e[idx])tmp[i] = pre[i]; forn(curr, 1, queryCnt) { if (auto [op,l,r,id] = qu[curr]; op == 1) { const int L1 = s[idx - 1]; //前一块边界 const int R2 = e[idx]; //当前块边界 //完全包括 if (l <= L1 and R2 <= r) { ++blockCnt; continue; } //部分包括,有交集,暴力 if (l <= R2 and r >= L1) { //先把之前的整块更新改了 allUpdate(idx, blockCnt); blockCnt = 0; //起点需要+1 forv(i, min(r,R2), max(l+1,L1+1))tmp[i] ^= tmp[i - 1]; } } else { //计算当前块的贡献 if (s[idx] <= l and l <= e[idx]) { allUpdate(idx, blockCnt); blockCnt = 0; ans[id] = tmp[l]; } } } allUpdate(idx, blockCnt); //处理还未处理的整块更新 forn(i, s[idx], e[idx])nxt[i] = tmp[i]; //下一轮的序列 } forn(i, 1, n)pre[i] = nxt[i]; forn(i, 1, queryCnt)if (qu[i].op == 2)cout << ans[qu[i].id] << endl; } inline void solve() { cin >> n >> q; siz = sqrt(n); cnt = (n + siz - 1) / siz; forn(i, 1, n)cin >> pre[i], pos[i] = (i - 1) / siz + 1; s[0] = 1; forn(i, 1, cnt)s[i] = (i - 1) * siz + 1, e[i] = i * siz; e[cnt] = n; int updateCnt = 0; //修改次数 int queryCnt = 0; //待处理的操作次数 forn(i, 1, q) { cin >> qu[++queryCnt].op; qu[queryCnt].id = i; if (qu[queryCnt].op == 1) { cin >> qu[queryCnt].l >> qu[queryCnt].r; if (++updateCnt == siz)update(queryCnt), updateCnt = 0, queryCnt = 0; } else cin >> qu[queryCnt].l; } update(queryCnt); forn(i, 1, n)cout << pre[i] << endl; } signed int main() { Spider //------------------------------------------------------ int test = 1; // read(test); cin >> test; test = 1; forn(i, 1, test)solve(); // while (cin >> n, n)solve(); // while (cin >> test)solve(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理