題目鏈結
分析:
之所以找到這道題是因為不想寫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...