bzoj2141 排隊(線段樹 splay)

2021-08-15 07:08:21 字數 3175 閱讀 8900

題目鏈結

分析:

之所以找到這道題是因為不想寫dp了

看到網上的題解都是:分塊+線段樹,樹狀陣列+線段樹。。。

立志自己想解法

一開始我在想,能不能用cdq搞

但是cdq版的動態逆序對好像是單點修改

實際上逆序對的個數就是每個數字形成的逆序對之和

而每個數會和在ta之前比ta大的數以及在ta之後比ta大的數形成逆序對

a: 152

468ni: 021

100swap(a[3],a[5])

a: 156

428ni: 022

330

就看上面這個例子把,交換的是a[3]和a[5],對於1~2和4~5,ta們形成的逆序對個數沒有改變

只有[3,5]這個區間內的數產生的逆序對個數改變了

逆序對的改變次數:區間內比a[3]大的數字個數-比a[3]小的個數+比a[5]小的個數-比a[5]大的個數+(a[3]和a[5]交換後能否形成新的逆序對)

也就是說,改變區間[x,y],對區間外的數沒有影響

逆序對的改變:區間內比x大的個數-比x小的個數+比y小的個數-比y大的個數+(x和y交換後能否形成新的逆序對)

詢問區間內比x大的數的個數,一下子就想到了splay

因為有區間操作,所以還要套一層線段樹

線段樹每乙個結點都是一棵splay,按照權值維護區間內的數

每次詢問:

1. x==y||a[x]==a[y]的時候直接輸出目前的逆序對數

2. 如果x+1!=y需要討論四種情況:

condition

operating

情況一將x與y調換後,會增加[x+1,y-1]中比x大的數的個數個逆序對

情況二將x與y調換後,會減少[x+1,y-1]中比x小的數的個數個逆序對

情況三將x與y調換後,會減少[x+1,y-1]中比y大的數的個數個逆序對

情況四將x與y調換後,會增加[x+1,y-1]中比y小的數的個數個逆序對

3. 討論x,y的關係,如果a[x]>a[y]則減少乙個逆序對,反之增加

4. 輸出目前的逆序對的個數,更新x與y位置的值

要保證x<=y

insert的時候手殘沒維護pre

mxx:區間內比x值大的數的個數

mnx:區間內比x值小的數的個數

mxy:區間內比y值大的數的個數

mny:區間內比y值小的數的個數

查詢區間內比乙個數大(小)的數有多少的時候,要遍歷一下這棵splay

不能偷懶。。。

#include

#include

#include

#include

using

namespace

std;

const

int inf=1e9;

const

int n=20003;

int a[n],n,m,v[n],c[n];

void add_c(int x,int z)

int ask_c(int x)

int ch[n*40][2],pre[n*40],size[n*40],cnt[n*40],v[n*40],top=0;

int root[n<<2],mxx,mnx,mxy,mny,ans=0,x,y;

int clear(int bh)

int get(int x)

void update(int x)

void rotate(int bh)

void splay(int &root,int bh,int mb)

void insert(int &root,int x)

int last=0,now=root;

while (1)

last=now;

now=ch[now][(v[now]1:0];

if (!now)

}}void change(int bh,int l,int r,int wz,int z)

int find_pm(int &root,int x) //查詢排名

}}int askmin(int root,int x)

}}int askmax(int root,int x)

}}void ask_splay(int &root,int x,int y)

void ask(int bh,int l,int r,int l,int r)

int mid=(l+r)>>1;

if (l<=mid) ask(bh<<1,l,mid,l,r);

if (r>mid) ask(bh<<1|1,mid+1,r,l,r);

}int fro(int &root)

void del(int &root,int x)

if (!ch[root][0]&&!ch[root][1])

if (!ch[root][0])

if (!ch[root][1])

int t=fro(root),k=root;

splay(root,t,0);

ch[root][1]=ch[k][1];

pre[ch[k][1]]=root;

clear(k); update(root);

return;

}void dele(int bh,int l,int r,int wz,int z)

int solve()

if (a[x]else an--;

dele(1,1,n,x,a[x]); dele(1,1,n,y,a[y]);

change(1,1,n,y,a[x]); change(1,1,n,x,a[y]);

swap(a[x],a[y]);

ans+=an;

return ans;

}int main()

printf("%d\n",ans);

scanf("%d",&m);

for (int i=1;i<=m;i++)

return

0;}

BZOJ2141 排隊(線段樹套Treap)

點此看題面 大致題意 給你乙個序列,每次交換兩個數,求每次操作後的逆序對個數。推薦先去看一下這道題目 洛谷3759 tjoi2017 不勤勞的圖書管理員 貌似是此題的公升級版 推薦先去學一學線段樹套 treap 當然你也可以學習 hl666 奆佬分塊狂踩樹套樹 做了上面給出的那道題目,這道題目就是一...

bzoj 2141 排隊 樹套樹

交換位置l,r,對答案產生的影響是l,r內 比l大的數個數 比l小的數個數 比r小的數個數 比r大的數的個數 l和r交換後應該 1或 1 求這個東西用樹套樹就可以 做資料結構做傻了,還有很多其他優秀的做法 include include include define maxn 1700005 usi...

bzoj 2141 排隊 (樹狀陣列套線段樹)

題目大意 給出乙個序列,每次交換兩個位置的數,求交換完後整個序列的逆序對數。對於乙個位置會產生的逆序對數是他前面比他大的數 他後面比他小的數。我們可以用樹狀陣列套線段樹維護一下,外層表示位置在樹狀陣列中該點的控制區間,線段樹是權值線段樹。然後每次交換完了計算一下就可以了。include includ...