time limit: 20 sec
memory limit: 128 mb
submit: 717
solved: 323 [
submit][
status][
discuss]
6 2160 163 164 161 167 160
2 3 63
1【題目思路】
每次操作是把乙個數後面所有小於等於該數值的位置都排序重新放置到原位置,那麼一次操作減少的逆序對個數就是該次操作調整的所有數構成的逆序對個數(就是每個點後面小於自己的數的個數的和)。
下面有幾點性質:
1、每次操作以後,所有統計過的點都不用再統計(且下次若是要操作這個點也不用操作),因為如果h[i]已經被操作過,並且到了j的位置,成了h1[j](即h1[j]==h[i]),那麼h1[j]後面的值肯定都是比h1[j]要大的,逆序對個數肯定不會改變。 若是h1[j]後面的值h1[k]是和h1[j]在同一操作中調整過的,那麼h1[k]>=h1[j],若是沒有調整過,那麼說明該值肯定大於h1[j](沒被調整過說明 該值》那次調整選擇的值》=h1[j])。
2、對於乙個沒被操作過的位置,無論後面的數如何操作,對於這個數而言這個數與後面的所有數構成的逆序對的個數不變。
所以每個位置都只會統計一次,預處理出所有的位置與後面的數構成的逆序對個數,然後每次操作,把統計過的點都標記,每次操作的時候只用統計沒被標記過的點。
用乙個線段樹就可以處理。
#include#include#include#include#define n 500005
#define ll long long
#define inf 1e9+7
using namespace std;
int tr[n*5],f[n],mark[n*5],n,m,h[n],x,mx=-inf;
ll ans=0;
struct hea[n];
void insert(int p,int l,int r,int x)
int mid=(l+r)/2;
insert(p*2,l,mid,x);insert(p*2+1,mid+1,r,x);
tr[p]=tr[p*2]+tr[p*2+1];
}int find(int p,int l,int r,int x,int y)
int mid=(l+r)/2;
modify(p*2,l,mid,x,y,u);modify(p*2+1,mid+1,r,x,y,u);
mark[p]=mark[p*2]|mark[p*2+1];
tr[p]=min(tr[p*2],tr[p*2+1]);
}bool cmp(he a, he b)
// memset(tr,0,sizeof(tr));
build(1,1,n);
printf("%lld\n",ans);
for(int i=1;i<=m;i++)
modify(1,1,n,x,n,h[x]);
printf("%lld\n",ans);
}}
BZOJ3333 排隊計畫(樹狀陣列 線段樹)
點此看題面 大致題意 給定乙個序列 a i 每次把 i sim n 中小於等於 a i 的數排序並放回原位置,求所有操作前及每一操作後的逆序對個數。眾所周知,排序的題目中必然有許多有趣的性質。我們定義乙個數的貢獻為它為較大值的逆序對個數。可以發現,根據此題的排序方式,不被排序的數與被排序的數之間的相...
bzoj2141 排隊(線段樹 splay)
題目鏈結 分析 之所以找到這道題是因為不想寫dp了 看到網上的題解都是 分塊 線段樹,樹狀陣列 線段樹。立志自己想解法 一開始我在想,能不能用cdq搞 但是cdq版的動態逆序對好像是單點修改 實際上逆序對的個數就是每個數字形成的逆序對之和 而每個數會和在ta之前比ta大的數以及在ta之後比ta大的數...
BZOJ2141 排隊(線段樹套Treap)
點此看題面 大致題意 給你乙個序列,每次交換兩個數,求每次操作後的逆序對個數。推薦先去看一下這道題目 洛谷3759 tjoi2017 不勤勞的圖書管理員 貌似是此題的公升級版 推薦先去學一學線段樹套 treap 當然你也可以學習 hl666 奆佬分塊狂踩樹套樹 做了上面給出的那道題目,這道題目就是一...