UVA11990 ``Dynamic‘‘ Inversion

UVA11990 ``Dynamic'' Inversion

  • 題目鏈接
  • 題意
    • 輸入格式
    • 輸出格式
  • 分析
    • CDQ分治
    • 嵌套(樹狀數組套BST)
    • 分塊
    • k-D Tree

題目鏈接

??UVA11990 ``Dynamic’’ Inversion

題意

??給一個 1~n 的排列A,要求按照某種順序刪除一些數(其他數順序不變),輸出每次刪除之前逆序對的數目。所謂逆序對數,就是滿足i<j 且A[i]>A[j]的有序對(i,j)的數目。

輸入格式

??輸入包含多組數據,每組數據的第一行為兩個整數n 和m(1≤n≤200 000,1≤m≤100 000);接下來的n 行表示初始排列;接下來的m 行按順序給出刪除的整數,每個整數保證不會刪除兩次。輸入結束標志為文件結束符(EOF)。輸入文件大小不超過5MB。

輸出格式

??對于每次刪除,輸出刪除之前的逆序對數。

分析

??核心思想是先求出初始逆序對個數,然后減去每次刪除元素后受影響的逆序對個數。初始逆序對個數利用樹狀數組可以求出。作為嵌套和分塊數據結構的經典題目,下面從CDQ分治、嵌套(本題是樹狀數組套BST)、分塊、k-D Tree這四種常見解法分別做分析。

CDQ分治

??考慮給每個元素三個屬性 t,p,vt,p,vt,p,v,分別表示這個元素的刪除時間 ttt,位置 ppp,值 vvv,那刪除元素 iii 受影響的逆序對 (i,j)(i,j)(i,j) 需要滿足以下條件 (ti<tj∧pi<pj∧vi>vj)∨(ti<tj∧pi>pj∧vi<vj)(t_i<t_j \wedge p_i<p_j \wedge v_i > v_j)\vee(t_i<t_j \wedge p_i>p_j \wedge v_i < v_j)(ti?<tj?pi?<pj?vi?>vj?)(ti?<tj?pi?>pj?vi?<vj?),套用CDQ分治處理三維偏序問題的思想即可解決:排序處理第一維 tit_iti?,分治處理第二維 pip_ipi?,樹狀數組處理第三維 viv_ivi?

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;#define N 200100
struct {int t, p, v;} e[N]; int a[N], p[N], c[N], f[N], m, n; long long s;bool cmp(int i, int j) {return e[i].t < e[j].t;
}void solve(int s, int t) {if (t - s == 1) return;int m = (s+t) / 2, h = m, q = t-1;solve(s, m); solve(m, t);for (int i=s; i<m; ++i) {while (h < t && e[a[h]].p < e[a[i]].p) for (int x=e[a[h++]].v; x; x -= x&-x) ++c[x];if (e[a[i]].t < ::m) for (int x=e[a[i]].v+1, j=e[a[i]].t; x<=n; x += x&-x) f[j] += c[x];}for (int i=m; i<h; ++i) for (int x=e[a[i]].v; x; x -= x&-x) --c[x];for (int i=m-1; i>=s; --i) {while (q >=m && e[a[q]].p > e[a[i]].p) for (int x=e[a[q--]].v; x<=n; x += x&-x) ++c[x];if (e[a[i]].t < ::m) for (int x=e[a[i]].v-1, j=e[a[i]].t; x; x -= x&-x) f[j] += c[x];}if (s == 0 && t == n) return;for (int i=t-1; i>q; --i) for (int x=e[a[i]].v; x<=n; x += x&-x) --c[x];for (int i=s; i<t; ++i) p[i] = a[i];for (int i=s, j=m, k=s; k<t; ++k) a[k] = i == m || (j < t && e[p[j]].p < e[p[i]].p) ? p[j++] : p[i++];
}void solve() {memset(c, s = 0, sizeof(c));for (int i=0; i<n; ++i) {cin >> e[i].v; e[i].p = p[e[i].v] = i; e[i].t = m; a[i] = i;for (int x = e[i].v+1; x<=n; x += x&-x) s += c[x];for (int x = e[i].v; x; x -= x&-x) ++c[x];}for (int i=0, v; i<m; ++i) cin >> v, f[e[p[v]].t = i] = 0;sort(a, a+n, cmp); memset(c, 0, sizeof(c)); solve(0, n);for (int i=0; i<m; s -= f[i++]) cout << s << endl;
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

嵌套(樹狀數組套BST)

??嵌套的解題思路引用自《訓練指南》410頁:

??令 larger(p,v)larger(p,v)larger(p,v) 表示 Ap>vA_p>vAp?>v 是否成立(1 表示成立,0 表示不成立),則“前 k 個數有多少個比 v 大”等于 larger(1,v)+larger(2,v)+larger(3,v)+…+larger(k,v)。
??注意,這是一個前綴查詢,因此可以用 Fenwick 樹維護。回憶第 3 章中對 Fenwick 樹的 C 數組的定義Ci=Ai?lowbit(i)+1+Ai?lowbit(i)+2+…+AiC_i=A_{i-lowbit(i)+1}+A_{i-lowbit(i)+2}+…+A_iCi?=Ai?lowbit(i)+1?+Ai?lowbit(i)+2?++Ai???對應到本題中,CiC_iCi? 的值實際上等于 “A(i?lowbit(i)+1),…,AiA_(i-lowbit(i)+1), …, A_iA(?i?lowbit(i)+1),,Ai? 中有多少個元素比 v 大”。考慮到本題還有刪除操作,可以用第3 章介紹過的名次樹來實現這些 CiC_iCi?。這樣,我們得到了一個 “Fenwick 樹套名次樹”的特殊數據結構,在O(log2n)O(log^2n)O(log2n)時間內解決了本題。
??具體來說,對于每個 iii,我們用 Ai?lowbit(i)+1,…,AiA_{i-lowbit(i)+1},…, A_iAi?lowbit(i)+1?,,Ai? 這些數構造第 iii 棵名次樹,支持兩個操作:刪除元素和統計樹里有多少個元素小于某個 kkk。當刪除第 iii 個位置的數v 時,按照 Fenwick 樹算法計算 Q(i?1)+…+Q(i?lowbit(i?1))…Q(i-1)+…+Q(i-lowbit(i-1))…Q(i?1)++Q(i?lowbit(i?1))這里的 Q(i)Q(i)Q(i) 就是查找第 iii 棵BST 中大于 vvv 的個數。
??這里的名次樹并不需要用 Treap 或者伸展樹實現,因為它只有刪除沒有插入和修改。如果給每個結點添加一個“是否已刪除”的標記,就可以在不改變樹結構的情況下支持刪除操作。換句話說,只要在預處理時把每棵名次樹都建成完全平衡的二叉樹,則兩個操作的時間復雜度總是對數級別的。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;#define N 200100
int a[N], p[N], c[N], e[N], m, n, t; long long s;
struct node {int v, d; node *l, *r;} q[N*18], *h[N];node* build(int a, int b) {if (a >= b) return NULL;node *r = q + t++; int m = (a+b)>>1; *r = {e[m], 0, build(a, m), build(m+1, b)};return r;
}int query(const node *p, int s, int v) {if (!p) return 0;if (p->v >= v) return query(p->l, s>>1, v);int ld = (p->l ? p->l->d : 0), c = p->d - ld - (p->r ? p->r->d : 0);return (s>>1) - ld + 1-c + query(p->r, s-1 - (s>>1), v);
}int query(int x) {int a = x-1, b = 0, e = 0;for (int i=x; i; i -= i&-i) a -= c[i];for (int i=p[x]-1; i; i -= i&-i) b += (i&-i) - h[i]->d, e += query(h[i], i&-i, x);return b-e + a-e;
}void remove(node *p, int v) {p->d ++;if (p->l && p->v > v) remove(p->l, v);if (p->r && v > p->v) remove(p->r, v);
}void solve() {memset(c, s = t = 0, sizeof(c));for (int i=1; i<=n; ++i) {cin >> a[i]; p[a[i]] = i;for (int x = a[i]+1; x<=n; x += x&-x) s += c[x];for (int x = a[i]; x; x -= x&-x) ++c[x];int cc = i&-i;for (int j=i-cc+1, k=0; j<=i; ++j) e[k++] = a[j];sort(e, e+cc);h[i] = q + t++; *h[i] = {e[cc>>1], 0, build(0, cc>>1), build((cc>>1)+1, cc)};}memset(c, 0, sizeof(c));while (m--) {int x; cin >> x;cout << s << endl;s -= query(x);for (int i=x; i<=n; i += i&-i) ++c[i];for (int i=p[x]; i<=n; i += i&-i) remove(h[i], x);}
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

分塊

??設將原序列分成長度為 sss 的塊,對于每個塊維護一個有序數組。每次刪除一個數,容易想到在它前面比它大的數和在它后面比它小的數的計數會被清除,所以對于每個塊二分查找個數并相加,對刪除的數所在的塊暴力統計即可。刪除時,直接在原數組中將其標記,有序數組中找到它并將其移至所在塊最后方。
??單次查詢的時間復雜度為 O(nslog?s+s)O(\frac n s \log s + s)O(sn?logs+s),單次刪除的時間復雜度為 O(s+log?s)O(s+\log s)O(s+logs),因此單次查詢及刪除總的時間復雜度為O(nslog?s+s)O(\frac n s \log s + s)O(sn?logs+s)
??本題限時3秒,分塊的大小s將成為卡常限制。如果取 s=ns=\sqrt ns=n? 則總的查詢和刪除的時間復雜度為O(mnlog?n)O(m\sqrt n \log \sqrt n)O(mn?logn?),提交容易超時;取 s=nms=\frac n {\sqrt m}s=m?n? 整體來說時間復雜度O(mmlog?nm+nm)O(m\sqrt m \log \frac n {\sqrt m} + n\sqrt m)O(mm?logm?n?+nm?)更優(因為1≤n≤200 000,1≤m≤100 000,m≤n)。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;#define N 200100
#define M 634
int b[M][M], a[N], p[N], c[N], t[M], m, n, y, z; long long s; bool f[N];inline int query(int v) {int s = 0, h = p[v] / z; f[p[v]] = true;for (int i=0; i<h; ++i) s += t[i] - (upper_bound(b[i], b[i]+t[i], v) - b[i]);for (int i=h+1; i<y; ++i) s += lower_bound(b[i], b[i]+t[i], v) - b[i];for (int i=z*h; i<p[v]; ++i) if (a[i] > v && !f[i]) ++s;for (int i=p[v]+1, j=min(z*h+z, n); i<j; ++i) if (a[i] < v && !f[i]) ++s;for (int i=lower_bound(b[h], b[h]+t[h], v) - b[h], j=--t[h]; i<j; ++i) b[h][i] = b[h][i+1];return s;
}void solve() {memset(c, s = 0, sizeof(c)); z = n/sqrt(m)+1; y = (n + z-1) / z;for (int i=0; i<n; ++i) {cin >> a[i]; f[p[a[i]] = i] = false; b[i/z][i%z] = a[i];for (int x = a[i]+1; x<=n; x += x&-x) s += c[x];for (int x = a[i]; x; x -= x&-x) ++c[x];}for (int i=0; i<y; ++i) t[i] = i+1 < y ? z : n - (y-1)*z, sort(b[i], b[i] + t[i]);while (m--) {cout << s << endl;int v; cin >> v;if (m == 0) return;s -= query(v);}
}int main() {ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);while (cin >> n >> m) solve();return 0;
}

k-D Tree

??待補充

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90849.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90849.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90849.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

銀河麒麟“安裝器”安裝方法

書接上回&#xff1a;銀河麒麟安裝軟件商店方法-CSDN博客 過了幾天發現當時一不小心把系統自帶的“安裝器”軟件也卸載掉了&#xff0c;導致現在deb文件只能通過命令行安裝&#xff0c;尋思這可不行&#xff0c;就想一下應該怎么安裝。 首先&#xff0c;為了確認一下安裝器的…

計算機畢設分享-基于SpringBoot的健身房管理系統(開題報告+前后端源碼+Lun文+開發文檔+數據庫設計文檔)

基于SpringBoot的健身房管理系統分享一套完整的基于SpringBoot的健身房管理系統畢業設計&#xff08;開題報告完整前后端源碼Lun文 開發文檔數據庫設計文檔&#xff09;系統分為三個角色功能如下&#xff1a;用戶功能需求描述管理員功能需求描述教練功能需求描述開題報告系統功…

代碼審計與web安全選擇題1

軟件供應鏈安全的基礎是&#xff08; &#xff09;A.完善的需求分析B.源代碼安全C.滲透測試D.軟件測試參考答案&#xff1a;B保證源代碼安全的主要措施包括&#xff08; &#xff09;A.開發工具和環境的安全B.代碼安全C.滲透測試D.代碼審計E.軟件的說明文檔完整參考…

python基本數據類型 數據類型轉換 數字 菜鳥教程筆記

python基本數據類型 數據類型轉換 數字 菜鳥教程筆記 1.基本數據類型 Python 中的變量不需要聲明。每個變量在使用前都必須賦值&#xff0c;變量賦值以后該變量才會被創建。 在 Python 中&#xff0c;變量就是變量&#xff0c;它沒有類型&#xff0c;我們所說的"類型"…

USRP X410 X440 5G及未來通信技術的非地面網絡(NTN)

概述 在本白皮書中&#xff0c;我們將介紹NTN的現狀、正處于探索階段的一些新應用&#xff0c;以及最重要的一點&#xff0c;我們需要克服哪些技術挑戰才能讓這個市場充滿活力。最后&#xff0c;我們將概述為實現實用高效的測試&#xff0c;NI圍繞NTN所做的努力&#xff0c;該測…

基于SpringBoot+Vue的電腦維修管理系統(WebSocket實時聊天、Echarts圖形化分析)

“ &#x1f388;系統亮點&#xff1a;WebSocket實時聊天、Echarts圖形化分析”01系統開發工具與環境搭建—前后端分離架構項目架構&#xff1a;B/S架構運行環境&#xff1a;win10/win11、jdk17小程序端&#xff1a;技術&#xff1a;Uniapp&#xff1b;UI庫&#xff1a;colorUI…

2025.7.28總結

今天真有點小煩&#xff0c;工作有些不太順利&#xff0c;我是真沒想到&#xff0c;阻塞我工作開展得竟然是我的主管。當初需求澄清的時候&#xff0c;開發說要申請一個便攜&#xff0c;我當時申請的時候也跟主管說了&#xff0c;需求測試的時候要使用到&#xff0c;但主管要我…

DBA常用數據庫查詢語句

1 數據庫信息 1.1 數據庫概要 select a.name "DB Name",e.global_name "Global Name",c.host_name "Host Name",c.instance_name "Instance Name" ,DECODE(c.logins,RESTRICTED,YES,NO) "Restricted Mode",a.log_mode &quo…

【c++深入系列】:萬字詳解priority_queue(附模擬實現的源碼)

&#x1f525; 本文專欄&#xff1a;c &#x1f338;作者主頁&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客勵志語錄&#xff1a; 真正的強大&#xff0c;不是從不跌倒&#xff0c;而是每次跌倒后都能笑著站起來 ★★★ 本文前置知識&#xff1a; 模版 引入 那么pri…

分享一個腳本,從mysql導出數據csv到hdfs臨時目錄

想從mysql導出一個表到csv文件&#xff0c;然后上傳到hdfs&#xff0c;開始使用sqoop&#xff0c;結果各種問題頻出&#xff1a; https://blog.csdn.net/weixin_45357522/article/details/149498030 https://blog.csdn.net/weixin_45357522/article/details/149449413 特別是那…

OpenLayers 綜合案例-區域掩膜

看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大家&#xff0c;助你少走彎路&#xff01; OpenLayers…

30天打牢數模基礎-神經網絡基礎講解

一、代碼說明本代碼基于模擬房價數據集&#xff0c;使用scikit-learn庫中的MLPRegressor&#xff08;多層感知器回歸&#xff09;實現神經網絡模型&#xff0c;解決房價預測問題。代碼邏輯清晰&#xff0c;適合數模小白入門&#xff0c;包含數據預處理、模型構建、訓練評估、新…

Linux應用開發基礎知識——LInux學習FreeType編程(七)

目錄 一、使用freetype 顯示一個文字 二、使用 freetype 顯示一行文字 1. 了解笛卡爾坐標系 2. 每個字符的大小可能不同 3. 怎么在指定位置顯示一行文字 4. freetype 的幾個重要數據結構 4.1、FT_Library結構體 4.2、FT_Face結構體 4.3、FT_GlyphSlot結構體 4.4、FT_G…

Kotlin中Flow

Kotlin Flow 深度解析&#xff1a;從原理到實戰一、Flow 核心概念體系1. Flow 的本質與架構Flow 是 Kotlin 協程庫中的異步數據流處理框架&#xff0c;核心特點&#xff1a;響應式編程&#xff1a;基于觀察者模式的數據處理協程集成&#xff1a;無縫融入 Kotlin 協程生態背壓支…

Java程序員學從0學AI(七)

一、前言 上一篇文章圍繞 Spring AI 的 Chat Memory&#xff08;聊天記憶&#xff09;功能展開&#xff0c;先是通過代碼演示了不使用 Chat Memory 時&#xff0c;大模型因無狀態無法記住上下文&#xff08;如用戶姓名&#xff09;的情況&#xff0c;隨后展示了使用基于內存的 …

ESP32S3 防貓逃脫監測系統

在辦公室里&#xff0c;兩只可愛的貓咪給大家帶來了不少歡樂&#xff0c;但其中一只總愛趁人不注意溜出房間&#xff0c;有時下班后還會被鄰居告知它被鎖在了外面。為了解決這個問題&#xff0c;我開發了一個基于 SeeedStudio XIAO ESP32S3 Sense 的貓咪逃脫監測預警系統&#…

Python|OpenCV-實現快速處理圖像的方法(23)

前言 本文是該專欄的第25篇,后面將持續分享OpenCV計算機視覺的干貨知識,記得關注。 在視覺算法落地流程中,數據預處理往往占用 60 % 以上的工程時間。以某沿海城市智慧旅游項目為例,我們從無人機錄制的 4K 海灘視頻中抽幀得到 10 000 張 PNG 原圖,分辨率 38402160,單張體…

Redis四種GetShell方式完整教程

Redis作為高性能內存數據庫&#xff0c;若未正確配置認證和訪問控制&#xff0c;可能被攻擊者利用實現遠程代碼執行&#xff08;GetShell&#xff09;。本文詳細講解四種常見的Redis GetShell方式&#xff0c;涵蓋原理、操作步驟及防御建議。方式一&#xff1a;直接寫入Shell腳…

clock_nanosleep系統調用及示例

41. clock_nanosleep - 高精度睡眠 函數介紹 clock_nanosleep系統調用提供納秒級精度的睡眠功能&#xff0c;支持絕對時間和相對時間兩種模式&#xff0c;比傳統的nanosleep更加靈活。 函數原型 #include <time.h>int clock_nanosleep(clockid_t clock_id, int flags,con…

用了Flutter包體積增大就棄用Flutter嗎?包體積與開發效率,這兩者之間如何權衡?

是否因包體積增大而棄用 Flutter&#xff0c;本質上是 “短期成本&#xff08;包體積&#xff09;” 與 “長期價值&#xff08;跨平臺效率、體驗一致性等&#xff09;” 的權衡 。這一決策沒有絕對答案&#xff0c;需結合項目階段、用戶群體、業務需求等具體場景分析。以下從核…