Codeforces 785E 題解(樹套樹-樹狀陣列套線段樹)
題目大意:
對於一個長度為n的序列進行k次操作,每次操作都是交換序列中的某兩個數。對於每一個操作,回答當前序列中有多少個逆序對。
題解:
每次更改序列都可以理解為,將答案減去被調換的位置原有數字的對答案的貢獻,然後調換兩數字的位置,然後將答案加上被調換的位置在調換之後的數字對答案的貢獻。
每一個數字對答案的貢獻都可以理解為在其出現位置之前的比它大的數字的個數加上在其出現位置之後的比它小的數字的個數。
由此,用樹狀陣列套線段樹可做。樹狀陣列是根據位置維護的,樹狀陣列的每個結點包含一個權值線段樹,但是直接開權值線段樹的話空間會爆,所以線段數要動態開點,那麼每一次修改只會至多使用log2n的空間。
同時也要注意,計算貢獻的時候要注意被調換的兩個數字之間的相互影響。
程式碼:
樹套樹版本
#include <cstdio>
#include <iostream>
using namespace std;
#define lowbit(k) (k&-(k))
const int maxn=int(2e5)+111;
int n,m;
struct Node {
int sum,ls,rs;
Node() {}
Node(int s,int l,int r):sum(s),ls(l),rs(r) {}
}node[maxn*200 ];
int root[maxn],tot=0;
void seg_modify(int k,int l,int r,int pos,int val) {
if(l==r && l==pos) {
node[k].sum=val;
return;
}
int mid=(l+r)>>1;
int &ls=node[k].ls,&rs=node[k].rs;
if(pos<=mid) {
if(!ls) ls=++tot;
seg_modify(ls,l,mid,pos ,val);
}
else {
if(!rs) rs=++tot;
seg_modify(rs,mid+1,r,pos,val);
}
node[k].sum=(ls?node[ls].sum:0)+(rs?node[rs].sum:0);
return;
}
int seg_query(int k,int l,int r,int ql,int qr) {
if(ql<=l && r<=qr) return node[k].sum;
int mid=(l+r)>>1;
int &ls=node[k].ls,&rs=node[k].rs;
if(qr<=mid) return ls?seg_query(ls,l,mid,ql,qr):0;
if(ql> mid) return rs?seg_query(rs,mid+1,r,ql,qr):0;
return (ls?seg_query(ls,l,mid,ql,qr):0)+(rs?seg_query(rs,mid+1,r,ql,qr):0);
}
void bit_build() {
for(int i=1;i<=n;i++) root[i]=i;
tot=n;
for(int i=1;i<=n;i++) {
int len=lowbit(i);
for(int j=i;j>=(i-len+1);j--) seg_modify(root[i],1,n,j,1);
}
return;
}
int bit_query(int pos,int l,int r) {
int res=0;
while(pos) {
res+=seg_query(root[pos],1,n,l,r);
pos-=lowbit(pos);
}
return res;
}
void bit_modify(int pos,int val,int dir) {
while(pos<=n) {
seg_modify(root[pos],1,n,val,dir);
pos+=lowbit(pos);
}
return;
}
int a[maxn];
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
scanf("%d%d",&n,&m);
bit_build();
long long ans=0;
for(int i=1;i<=n;i++) a[i]=i;
while(m--) {
int x,y;
scanf("%d%d",&x,&y);
int g1=bit_query(n,1,a[x]-1)-bit_query(x,1,a[x]-1)+bit_query(x-1,a[x]+1,n);
bit_modify(x,a[x],0);
int g2=bit_query(n,1,a[y]-1)-bit_query(y,1,a[y]-1)+bit_query(y-1,a[y]+1,n);
bit_modify(y,a[y],0);
ans-=g1+g2;
swap(a[x],a[y]);
bit_modify(x,a[x],1);
int c1=bit_query(n,1,a[x]-1)-bit_query(x,1,a[x]-1)+bit_query(x-1,a[x]+1,n);
bit_modify(y,a[y],1);
int c2=bit_query(n,1,a[y]-1)-bit_query(y,1,a[y]-1)+bit_query(y-1,a[y]+1,n);
ans+=c1+c2;
printf("%I64d\n",ans);
}
return 0;
}
分塊套樹狀陣列版本(lewin)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
// taken from here: https://github.com/igrsk/spoj/blob/master/SWAPS.cpp
#define FORO(i,n) for(int i = 0;i < n;i++)
#define FORI(i,n) for(int i = 1;i <= n;i++)
const int MAXN = 200000;
const int MAXA = 200000;
const int MAXSQRTN = 500;
int A[MAXN];
int N, M;
int bit[MAXSQRTN+1][MAXA+1];
int sqrtN;
void bitinc(int i,int v,int *d) {
for(;i <= MAXA;i += i&-i) d[i]+=v;
}
void bitins(int x,int y,int v) {
while(x <= sqrtN) {
bitinc(y,v,bit[x]);
x += x&-x;
}
}
int bitsum(int x,int y) {
int ret = 0;
for(;x > 0;x-=x&-x)
for(int yy = y;yy > 0;yy-=yy&-yy)
ret += bit[x][yy];
return ret;
}
void init() {
for(sqrtN = 1;sqrtN*sqrtN < N;sqrtN++) ;
for(int i = 0;i < N;i++) {
bitins(i/sqrtN+1,A[i],1);
}
}
int query(int i,int x) {
int ret = bitsum(i/sqrtN,x);
// 0 ~ sqrtN-1, sqrtN ~ 2*sqrtN-1, ...
for(int j = sqrtN*(i/sqrtN);j <= i;j++)
if(A[j] <= x) ret++;
return ret;
}
int main() {
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d %d",&N, &M);
FORO(i,N) A[i] = i+1;
init();
long long orans = 0;
FORO(i,N) {
orans += i-query(i,A[i])+1;
}
cout<<"orans:"<<orans<<endl;
int X, Y, q1, q2;
FORO(mm,M) {
scanf("%d %d",&X,&Y);
// update and query
X--; Y--;
if (X != Y) {
q1 = A[X];
q2 = A[Y];
orans -= (X-query(X-1,A[X])-1)+(query(N-1,A[X]-1)-query(X,A[X]-1));
bitins(X/sqrtN+1,A[X],-1);
A[X] = q2;
bitins(X/sqrtN+1,q2,1);
orans += (X-query(X-1,q2)-1)+(query(N-1,q2-1)-query(X,q2-1));
orans -= (Y-query(Y-1,A[Y])-1)+(query(N-1,A[Y]-1)-query(Y,A[Y]-1));
bitins(Y/sqrtN+1,A[Y],-1);
A[Y] = q1;
bitins(Y/sqrtN+1,q1,1);
orans += (Y-query(Y-1,q1)-1)+(query(N-1,q1-1)-query(Y,q1-1));
}
printf("%lld\n",orans);
}
return 0;
}
分塊套vector版本(kmjp)
#include <bits/stdc++.h>
using namespace std;
typedef signed long long ll;
#undef _P
#define _P(...) (void)printf(__VA_ARGS__)
#define FOR(x,to) for(x=0;x<(to);x++)
#define FORR(x,arr) for(auto& x:arr)
#define ITR(x,c) for(__typeof(c.begin()) x=c.begin();x!=c.end();x++)
#define ALL(a) (a.begin()),(a.end())
#define ZERO(a) memset(a,0,sizeof(a))
#define MINUS(a) memset(a,0xff,sizeof(a))
//-------------------------------------------------------
int N,Q;
int A[201010];
int L,R;
const int D=500;
vector<int> V[500];
void erase(int id,int v) {
int b=id/D;
int i;
FOR(i,V[b].size()) if(V[b][i]==v) {
V[b].erase(V[b].begin()+i);
return;
}
}
void add(int id,int v) {
int b=id/D;
int i;
FOR(i,V[b].size()) if(v<V[b][i]) {
V[b].insert(V[b].begin()+i,v);
return;
}
V[b].push_back(v);
}
int getmore(int id,int v) {
int i,j;
int ret=0;
FOR(i,500) {
if(id<(i+1)*D) {
for(j=i*D;j<id;j++) if(A[j]>v && A[j]!=1<<20) ret++;
break;
}
else {
ret += V[i].end()-lower_bound(ALL(V[i]),v);
}
}
return ret;
}
int getless(int id,int v) {
int i,j;
int ret=0;
FOR(i,500) {
if(id<(i+1)*D) {
for(j=i*D;j<id;j++) if(A[j]<v) ret++;
break;
}
else {
ret += lower_bound(ALL(V[i]),v)-V[i].begin();
}
}
return ret;
}
void solve() {
int i,j,k,l,r,x,y; string s;
cin>>N>>Q;
FOR(i,N) {
A[i]=i+1;
add(i,A[i]);
}
ll ret=0;
while(Q--) {
cin>>L>>R;
L--,R--;
if(L==R) {
cout<<ret<<endl;
continue;
}
if(L>R) swap(L,R);
if(A[L]<A[R]) ret--;
else ret++;
ret-=getmore(R,A[R]);
ret-=A[R]-1-getless(R,A[R]);
ret-=getmore(L,A[L]);
ret-=A[L]-1-getless(L,A[L]);
erase(R,A[R]);
erase(L,A[L]);
swap(A[L],A[R]);
add(R,A[R]);
add(L,A[L]);
ret+=getmore(R,A[R]);
ret+=A[R]-1-getless(R,A[R]);
ret+=getmore(L,A[L]);
ret+=A[L]-1-getless(L,A[L]);
cout<<ret<<endl;
}
}
int main(int argc,char** argv){
string s;int i;
if(argc==1) ios::sync_with_stdio(false), cin.tie(0);
FOR(i,argc-1) s+=argv[i+1],s+='\n';
FOR(i,s.size()) ungetc(s[s.size()-1-i],stdin);
solve(); return 0;
}
相關推薦
Codeforces 785E 題解(樹套樹-樹狀陣列套線段樹)
題目大意: 對於一個長度為n的序列進行k次操作,每次操作都是交換序列中的某兩個數。對於每一個操作,回答當前序列中有多少個逆序對。 題解: 每次更改序列都可以理解為,將答案減去被調換的位置原有數字的對答案的貢獻,然後調換兩數字的位置,然後將答案加上被調換
樹狀陣列與線段樹(二)
樹狀陣列 1.小朋友排隊 n 個小朋友站成一排。 現在要把他們按身高從低到高的順序排列,但是每次只能交換位置相鄰的兩個小朋友。 每個小朋友都有一個不高興的程度。 開始的時候,所有小朋友的不高興程度都是 0。 如果某個小朋友第一次被要求交換,則他的不高興程度增加 1,如果第二次要求
樹狀陣列與線段樹(三)
找規律題 1.螺旋折線 如下圖所示的螺旋折線經過平面上所有整點恰好一次。 對於整點 (X,Y),我們定義它到原點的距離 dis(X,Y) 是從原點到 (X,Y) 的螺旋折線段的長度。 例如 dis(0,1)=3,dis(−2,&minu
逆序對的三種求法(歸併排序,樹狀陣列,線段樹)
求逆序對個數的三種方法 逆序對: 對於一個序列 $a_1$,$a_2$,$a_3$..$a_n$,如果存在$a_i$>$a_j$且i<j,則$a_i$和$a_j$為一個逆序對。 這裡將介紹3種求逆序對對數的方法。 在此之前,預設為你已經會了歸併排序,樹狀陣列和線段樹。(不會的可以百度學習一下)
Codeforces 1093G題解(線段樹維護k維空間最大曼哈頓距離)
題意是,給出n個k維空間下的點,然後q次操作,每次操作要麼修改其中一個點的座標,要麼查詢下標為[l,r]區間中所有點中兩點的最大曼哈頓距離。 思路:參考blog:https://blog.csdn.net/Anxdada/article/details/81980574,裡面講了k維空間中的
2018.11.07【CQOI2011】【BZOJ3295】【洛谷P3157】動態逆序對(樹狀陣列套動態開點線段樹)
BZOJ傳送門 洛谷傳送門 解析: 首先我們可以通過一個線段樹求出逆序對個數,然後就是亂搞的時間了。 顯然每次刪除一個數,需要我們查詢前面比他大的數的個數和後面比他小的數的個數,這個就是裸的樹套樹了。這道題可以用樹狀陣列套線段樹動態開點。 程式碼: #
CF1093:E. Intersection of Permutations(樹狀陣列套主席樹)
題意:給定長度為N的a陣列,和b陣列,a和b都是1到N的排列; 有兩種操作,一種是詢問[L1,R1],[L2,R2];即問a陣列的[L1,R1]區間和b陣列的[L2,R2]區間出現了多少個相同的數字。 一種是修改b陣列兩個位置的值。 思路:如果把b陣列每個數取對應a陣列對應數的位置,即按照b的下標建立橫座標
Bestcoder7(1004)hdu4988(經典問題:樹狀陣列套treap求解動態逆序對)
Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(
CodeForces 594D REQ(樹狀陣列+尤拉函式)
題意:給出一個序列,有m個詢問,每次詢問要求回答一個區間內所有數的乘積的尤拉函式。 思路:這道題看上去像是一個莫隊,但是莫隊的時間複雜度為O(n∗sqrt(n)∗k),其中k是序列中每個數的質因子的數量的最大值,這樣做會超時。 考慮樹狀陣列,因為我們要求的是
ZOJ 2112 Dynamic Rankings(主席樹套樹狀陣列+靜態主席樹)
題意:給定一個區間,求這個區間第k小的數,支援單點修改。 思路:主席樹真是個神奇的東西.........速度很快但是也有一個問題就是佔用記憶體的很大,一般來說支援單點修改的主席樹套樹狀陣列空間複雜度為O(n*logn*logn), 如果查詢較少的話,可以初始的時候用一顆靜態
hdu 1166 敵兵布陣——(區間和)樹狀數組/線段樹
har stdio.h 二叉 chang .net pre 計算機 大小 sta here:http://acm.hdu.edu.cn/showproblem.php?pid=1166 Input 第一行一個整數T。表示有T組數據。 每組數據第一行一個正整
【bzoj4785】[Zjoi2017]樹狀數組 線段樹套線段樹
奇怪 原因 tdi function 數字 二進制位 操作 接下來 還需 題目描述 漆黑的晚上,九條可憐躺在床上輾轉反側。難以入眠的她想起了若幹年前她的一次悲慘的OI 比賽經歷。那是一道基礎的樹狀數組題。給出一個長度為 n 的數組 A,初始值都為 0,接下來進行 m 次操
Codeforces Round #423 (Div. 2) C 思維,並查集 或 線段樹 D 樹構造,水
closed alt pda memset sed () back ref cup Codeforces Round #423 (Div. 2, rated, based on VK Cup Finals) C. String Reconstruction 思維,並查
「CodePlus 2017 11 月賽」Yazid 的新生舞會(樹狀數組/線段樹)
sizeof sum read 開頭 turn 單點 delta pac 圖片 學習了新姿勢。。(一直看不懂大爺的代碼卡了好久T T 首先數字範圍那麽小可以考慮枚舉眾數來計算答案,設當前枚舉到$x$,$s_i$為前$i$個數中$x$的出現次數,則滿足$2*s_r-
Educational Codeforces Round 37 (Rated for Div. 2)F. SUM and REPLACE+線段樹
namespace ted amp return Education span num sign define 題目鏈接:F. SUM and REPLACE 題意:給一個數組,兩種操作,第一種把[L,R]的數變成這個數的因子個數(這個是log級別的下降),第二種求[L,
bzoj4285 使者 樹狀陣列套線段樹
Description 公元 8192 年,人類進入星際大航海時代。在不懈的努力之下,人類佔領了 宇宙中的 n 個行星,並在這些行星之間修建了 n - 1 條星際航道,使得任意兩個 行星之間可以通過唯一的一條路徑互相到達。 同時,在宇宙中還有一些空間跳躍點,有些跳躍點已經被發現
藍書(演算法競賽進階指南)刷題記錄——POJ3468 A Simple Problem with Intergers(樹狀陣列維護差分)
題目:poj3468. 題目大意:給定一個序列a,要求支援: 1.格式C a b c,表示將[a,b]的權值都加上c. 2.格式Q a b,表示查詢[a,b]的權值和. 線段樹裸題(我像個傻子一樣寫了個LCT做了一遍),可是我們這裡不用線段樹,我們討論樹狀陣列的解法. 我們已
BZOJ4999:This Problem Is Too Simple!(DFS序&樹上差分&線段樹動態開點:區間修改單點查詢)
Description 給您一顆樹,每個節點有個初始值。 現在支援以下兩種操作: 1. C i x(0<=x<2^31) 表示將i節點的值改為x。 2. Q i j x(0<=x<2^31) 表示詢問i節點到j節點的路徑上有多少個
luogu3759 不勤勞的圖書管理員 (樹狀陣列套線段樹)
交換的話,只有它們中間的書會對答案產生影響 樹狀陣列記位置,套線段樹記書的編號 它對應的頁數和書的個數 然後就是減掉中間那些原來是逆序對的,再把交換以後是逆序對的加上 別忘了考慮這兩個自己交換以後是不是逆序的 最重要的一步:開個O2 1 #include<bits/stdc++.h&
[BZOJ 3196][模板] 二(遮蔽)平衡樹 - 樹狀陣列套主席樹
主席樹維護每個點的字首權值情況. 樹狀陣列維護區間. 想法比較直觀, 聯賽的教訓是想到了要能快寫. #include <iostream> #include <algorithm> #include <cstring> #include <cstdio>