bzoj3259动态逆序对
http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题意:
对于序列A,它的逆序对数定义为满足ij小于,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
tip:
先树状数组处理一下逆序对,然后每次删除元素,删除一个元素后,原本后面比他小的元素和他产生的逆序对就没了,原来前面比他大的对她产生的逆序对也可以剪掉了。如果快速做这个事情呢
对区间做个线段树,每个节点上保存一个treap ,这样在找:这个要删的点右侧且比这个值小的有多少,log区间,每个区间在treap上差值也是log的,所以查询操作就是loglog 然后对于删除,删除这个点所在log个区间上的treap里面这个值,复杂度还是loglog
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int N,Q,op;
const int maxn = 4e5+10;
const int maxm = 2e6+10;
int a[maxn],pos[maxn],root[maxn*4];
LL ans,arr[maxn];
int read() {
char c;
int ret = 0;
while ((c = getchar()) < '0' || c > '9');
while (c >= '0' && c <= '9'){
ret = ret * 10 + (c - '0'), c = getchar();
}
return ret;
}
struct Tree{
int l,r;
}tree[4*maxn];
struct Node{
int l,r,rnd;
long long key,_size,num;
void init(){
l = r = key = _size = rnd = num = 0;
}
};
class Treap{
public:
Node node[maxm] ;LL cnt;
void update(int k){
node[k]._size = node[node[k].l]._size + node[node[k].r]._size + node[k].num;
}
void lturn(int &k){
int t = node[k].r;node[k].r = node[t].l;node[t].l = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void rturn(int &k){
int t = node[k].l;node[k].l = node[t].r;node[t].r = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void _insert(int &k,int key){
if(k == 0){
++cnt;
node[cnt].init();k = cnt;node[cnt]._size = node[cnt].num = 1;node[cnt].rnd = rand();node[cnt].key = key;
return;
}
node[k]._size++;
if(node[k].key == key) node[k].num++;
else if(node[k].key < key){
_insert(node[k].r,key);
if(node[node[k].r].rnd < node[k].rnd) lturn(k);
}
else{
_insert(node[k].l,key);
if(node[node[k].l].rnd < node[k].rnd) rturn(k);
}
}
void del(int &k,int x){
if(k == 0) return;
if(node[k].key == x){
if(node[k].num > 1){
node[k].num--;node[k]._size--;return;
}
if(node[k].l*node[k].r == 0) k = node[k].l+node[k].r;
else if(node[node[k].l].rnd < node[node[k].r].rnd)
rturn(k),del(k,x);
else lturn(k),del(k,x);
}
else if(x > node[k].key)
node[k]._size--,del(node[k].r,x);
else node[k]._size--,del(node[k].l,x);
}
LL query_rank(int k,int x){
if(k == 0)return 0;
if(node[k].key == x) return node[node[k].l]._size+1;
else if(x > node[k].key){
//cout << "node[node[k].l]._size = "<<node[node[k].l]._size<<endl;
return node[node[k].l]._size+node[k].num+query_rank(node[k].r,x);
}
else return query_rank(node[k].l,x);
}
LL getsize(int k){
return node[k]._size;
}
}treap;
void build(int l,int r,int rt){
tree[rt].l = l;tree[rt].r = r;
int tmp_root = 0;
for(int i = l ; i <= r; i++){
// cout <<" "<<tmp_root <<"a "<< a[i]<<endl;
treap._insert(tmp_root,a[i]);
root[rt] = tmp_root;
// cout << "rt = "<< rt<<" l = "<<l<<" r = "<<r <<" root = "<<tmp_root<<endl;
}
if(l == r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int lowbit(int x){
return x&(-x);
}
void add(int pos,int num){
for(int i = pos ; i <= N ;i += lowbit(i)){
arr[i] += num;
}
}
int getsum(int pos){
LL sum = 0;
for(int i = pos; i > 0 ;i -= lowbit(i)){
sum += arr[i];
}
return sum;
}
void init(){
ans = 0;treap.cnt = 0;
for(int i = 1; i <= N ; i++){
a[i] = read();
pos[a[i]] = i;
arr[a[i]] = 0;
}
build(1,N,1);
for(int i = 1; i <= N ; i++){
ans += getsum(N)-getsum(a[i]);
add(a[i],1);
}
}
void update(int p,int l,int r,int rt){
if(p >= l && p <= r){
treap.del(root[rt],a[p]);
}
if(l == r) return;
int m=(l+r)>>1;
if(p <= m) update(p,lson);
else update(p,rson);
}
LL queryl(int L,int R,int l,int r,int rt,int op){
if( L <= l && R >= r){
int rk = treap.query_rank(root[rt],op);
return treap.getsize(root[rt])-rk;
}
int m=(l+r)>>1;
LL k = 0;
if(m >= L) k += queryl(L,R,lson,op);
if(m < R ) k += queryl(L,R,rson,op);
return k;
}
LL querys(int L,int R,int l,int r,int rt,int op){
if( L <= l && R >= r){
int rk = treap.query_rank(root[rt],op);
return rk;
}
int m=(l+r)>>1;
LL k = 0;
if(m >= L) k += querys(L,R,lson,op);
if(m < R ) k += querys(L,R,rson,op);
return k;
}
void sov(){
for(int i = 1; i <= Q ; i++){
op = read();
printf("%lld\n",ans);
if(pos[op]-1 > 0 )
ans -= queryl(1,pos[op]-1,1,N,1,op);//左侧大于她
if(pos[op]+1 <= N)
ans -= querys(pos[op]+1,N,1,N,1,op);//右侧小于他
update(pos[op],1,N,1);//删除
}
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d%d ",&N,&Q)){
init();
sov();
}
}
bzoj2141 排队
http://www.lydsy.com/JudgeOnline/problem.php?id=2141
题意:
求逆序对数量,每次可以交换两个
tip:
仍然是线段树套treap,预处理逆序对数,然后每次交换的时候,如果两个值相等,不用换了。
如果ai>bi逆序对-1 否则+1…
如果两个位置只差一个,那么不再处理
如果离得比较远,那么中间的位置,ans-比ai小的数量+比ai大的,ans-比bi大的+比bi小的
于是处理这个还是对位置做外层的线段树,每个线段树上都是一个treap节点,可以快速找。
交换ab我做的事是 删a 加b 删b 加a
三个wa点:
1.离散化,树状数组预处理的时候,
2.因为可以是相同的,所以treap里面处理大小的时候注意一下
3.输出的ai bi不一定ai小
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
typedef pair<LL ,int> pii;
int N,Q,op;
const int maxn = 2e5+10;
const int maxm = 2e6+10;
int a[maxn],pos[maxn],root[maxn*4];
LL ans,arr[maxn];
pii l[maxn];
map<LL,int>mp;
struct Tree{
int l,r;
}tree[4*maxn];
struct Node{
int l,r,rnd;
long long key,_size,num;
void init(){
l = r = key = _size = rnd = num = 0;
}
};
class Treap{
public:
Node node[maxm] ;LL cnt;
void update(int k){
node[k]._size = node[node[k].l]._size + node[node[k].r]._size + node[k].num;
}
void lturn(int &k){
int t = node[k].r;node[k].r = node[t].l;node[t].l = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void rturn(int &k){
int t = node[k].l;node[k].l = node[t].r;node[t].r = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void _insert(int &k,int key){
if(k == 0){
++cnt;
node[cnt].init();k = cnt;node[cnt]._size = node[cnt].num = 1;node[cnt].rnd = rand();node[cnt].key = key;
return;
}
node[k]._size++;
if(node[k].key == key) node[k].num++;
else if(node[k].key < key){
_insert(node[k].r,key);
if(node[node[k].r].rnd < node[k].rnd) lturn(k);
}
else{
_insert(node[k].l,key);
if(node[node[k].l].rnd < node[k].rnd) rturn(k);
}
}
void del(int &k,int x){
if(k == 0) return;
if(node[k].key == x){
if(node[k].num > 1){
node[k].num--;node[k]._size--;return;
}
if(node[k].l*node[k].r == 0) k = node[k].l+node[k].r;
else if(node[node[k].l].rnd < node[node[k].r].rnd)
rturn(k),del(k,x);
else lturn(k),del(k,x);
}
else if(x > node[k].key)
node[k]._size--,del(node[k].r,x);
else node[k]._size--,del(node[k].l,x);
}
LL query_rank(int k,int x){
if(k == 0)return 0;
if(node[k].key == x) return node[node[k].l]._size;
else if(x > node[k].key){
//cout << "node[node[k].l]._size = "<<node[node[k].l]._size<<endl;
return node[node[k].l]._size+node[k].num+query_rank(node[k].r,x);
}
else return query_rank(node[k].l,x);
}
LL query_rankb(int k,int x){
if(k == 0)return 0;
if(node[k].key == x) return node[node[k].r]._size;
else if(x < node[k].key){
return node[node[k].r]._size+node[k].num+query_rankb(node[k].l,x);
}
else return query_rankb(node[k].r,x);
}
LL getsize(int k){
return node[k]._size;
}
}treap;
void build(int l,int r,int rt){
tree[rt].l = l;tree[rt].r = r;
int tmp_root = 0;
for(int i = l ; i <= r; i++){
// cout <<" "<<tmp_root <<"a "<< a[i]<<endl;
treap._insert(tmp_root,a[i]);
root[rt] = tmp_root;
}
if(l == r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
int lowbit(int x){
return x&(-x);
}
void add(int pos,int num){
for(int i = pos ; i <= N ;i += lowbit(i)){
arr[i] += num;
}
}
int getsum(int pos){
LL sum = 0;
for(int i = pos; i > 0 ;i -= lowbit(i)){
sum += arr[i];
}
return sum;
}
void init(){
ans = 0;treap.cnt = 0;
for(int i = 1; i <= N ; i++){
scanf("%d",&a[i]);
l[i] = make_pair(a[i],i);
}
sort(l+1,l+1+N);
for(int i = 1; i <= N ; i++)
mp[l[i].second] = i;
memset(arr,0,sizeof(arr));
build(1,N,1);
for(int i = 1; i <= N ; i++){
// cout <<"get n "<<getsum(N)<<" getsu "<< getsum(mp[i])<<endl;
ans += getsum(N)-getsum(mp[i]);
add(mp[i],1);
}
printf("%lld\n",ans);
}
LL queryl(int L,int R,int l,int r,int rt,int op){
if( L <= l && R >= r){
int rk = treap.query_rankb(root[rt],op);
return rk;
}
int m=(l+r)>>1;
LL k = 0;
if(m >= L) k += queryl(L,R,lson,op);
if(m < R ) k += queryl(L,R,rson,op);
return k;
}
void update_del(int p,int l,int r,int rt){
if(p >= l && p <= r){
treap.del(root[rt],a[p]);
}
if(l == r) return;
int m=(l+r)>>1;
if(p <= m) update_del(p,lson);
else update_del(p,rson);
}
void update_in(int p,int l,int r,int rt,int val){
if(p >= l && p <= r){
treap._insert(root[rt],val);
}
if(l == r) return ;
int m = (l+r)>>1;
if(p <= m) update_in(p,lson,val);
else update_in(p,rson,val);
}
LL querys(int L,int R,int l,int r,int rt,int op){
if( L <= l && R >= r){
int rk = treap.query_rank(root[rt],op);
return rk;
}
int m=(l+r)>>1;
LL k = 0;
if(m >= L) k += querys(L,R,lson,op);
if(m < R ) k += querys(L,R,rson,op);
return k;
}
void sov(){
scanf("%d",&Q);
for(int i = 1; i <= Q ; i++){
int ai,bi;
scanf("%d%d",&ai,&bi);
if(ai > bi) swap(ai,bi);
if(a[ai] == a[bi]){
printf("%lld\n",ans);
continue;
}
if(a[ai] > a[bi]) ans--;
else ans++;
if(ai + 1 < bi){
// cout << "ans = "<<ans<<endl;
int y = querys(ai+1,bi-1,1,N,1,a[ai]);
int x = queryl(ai+1,bi-1,1,N,1,a[ai]);
ans = ans-y+x;
// cout <<" xiao = "<<y<<" da = "<<x<<" a = "<<ans<<endl;
y = querys(ai+1,bi-1,1,N,1,a[bi]);
x = queryl(ai+1,bi-1,1,N,1,a[bi]);
ans = ans + y-x;
// cout <<" xiao = "<< y <<" da = "<< x <<" as = "<<ans<<endl;
}
update_del(ai,1,N,1);//删除
update_in(ai,1,N,1,a[bi]);
update_del(bi,1,N,1);
update_in(bi,1,N,1,a[ai]);
swap(a[ai],a[bi]);
printf("%lld\n",ans);
}
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
while(~scanf("%d",&N)){
init();
sov();
}
}
bzoj3110 k大数查询
https://vijos.org/d/newbzoj/p/590c993fd3d8a13210993aa4
题意:
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
tip:
线段树上套两个treap ,
treap1表示这个范围上的某些位置上加了的数(多少个,treap里的num)
treap2 表示这个范围内的所有位置上都加了什么数,把这个数加到treap2上。
首先加入数的时候,走线段树上的区间,只有当这个区间(l,r)完全小于被整体加的区间的时候,加到treap2上,且这个时候一定return,结束递归(就是代替push_down用的)
如果线段树这个节点的区间和我更新的有交集或者完全大于我要更新的这个范围,那么在treap1上加这个权值,且交集多大就加多少个,
这样做之后,在query的时候,走到这个区间,如果这个区间完全大于我要查询的区间或者有交集,那么这个区间内一起加过的值肯定会在我查询的区间中都加了,也就是交集大小个,如果这个区间完全小于我要查询的区间(或者等于),这时候就看treap1 ,这个区间每个加过的元素肯定都在我要查询的里面。这时候也该return了。
问的是第k大得数,二分答案,但是这个数字可能没被添加过,所以如果严格大于mid的数有小于k个且严格大于mid-1的大于k个,就说名这个mid一定是答案了 return。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long LL;
const int maxn = 1e6+10;
int N,Q,head[maxn*4],tot,root1[maxn],root2[maxn];
struct Tree{
int l,r;
}tree[maxn*4];
struct Node{
int l,r,rnd;
long long key,_size,num;
void init(){
l = r = key = _size = rnd = num = 0;
}
};
class Treap{
public:
Node node[maxn*4] ;LL cnt;
void update(int k){
node[k]._size = node[node[k].l]._size + node[node[k].r]._size + node[k].num;
}
void lturn(int &k){
int t = node[k].r;node[k].r = node[t].l;node[t].l = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void rturn(int &k){
int t = node[k].l;node[k].l = node[t].r;node[t].r = k;
node[t]._size = node[k]._size; update(k);k = t;
}
void _insertmul(int &k,int key,int addnum){
if(k == 0){
++cnt;
node[cnt].init();k = cnt;node[cnt]._size = node[cnt].num = addnum;node[cnt].rnd = rand();node[cnt].key = key;
return;
}
node[k]._size += addnum;
if(node[k].key == key) node[k].num+=addnum;
else if(node[k].key < key){
_insertmul(node[k].r,key,addnum);
if(node[node[k].r].rnd < node[k].rnd) lturn(k);
}
else{
_insertmul(node[k].l,key,addnum);
if(node[node[k].l].rnd < node[k].rnd) rturn(k);
}
}
void del(int &k,int x){
if(k == 0) return;
if(node[k].key == x){
if(node[k].num > 1){
node[k].num--;node[k]._size--;return;
}
if(node[k].l*node[k].r == 0) k = node[k].l+node[k].r;
else if(node[node[k].l].rnd < node[node[k].r].rnd)
rturn(k),del(k,x);
else lturn(k),del(k,x);
}
else if(x > node[k].key)
node[k]._size--,del(node[k].r,x);
else node[k]._size--,del(node[k].l,x);
}
LL query_rank(int k,int x){
if(k == 0)return 0;
if(node[k].key == x) return node[node[k].l]._size;
else if(x > node[k].key){
//cout << "node[node[k].l]._size = "<<node[node[k].l]._size<<endl;
return node[node[k].l]._size+node[k].num+query_rank(node[k].r,x);
}
else return query_rank(node[k].l,x);
}
LL query_rankb(int k,int x){
if(k == 0)return 0;
if(node[k].key == x) return node[node[k].r]._size;
else if(x < node[k].key){
return node[node[k].r]._size+node[k].num+query_rankb(node[k].l,x);
}
else return query_rankb(node[k].r,x);
}
LL getsize(int k){
return node[k]._size;
}
}treap;
void build(int l,int r,int rt){
tree[rt].l = l;tree[rt].r = r;root1[rt] = root2[rt] = 0;
if(l == r){
return ;
}
int m = (l+r) >> 1;
build(lson);
build(rson);
}
void update(int L,int R,int l,int r,int rt,int num){//插入num
if(l >= L &&R >= r){
treap._insertmul(root2[rt],num,1);
return;
}
if(l <= L && R <= r){
treap._insertmul(root1[rt],num,R-L+1);
}
else if(r < R &&r >= L){
treap._insertmul(root1[rt],num,r-L+1);
}
else if(l > L &&l <= R){
treap._insertmul(root1[rt],num,R-l+1);
}
int m= (l+r)>>1;
if(m >= L)
update(L,R,lson,num);
if(m < R)
update(L,R,rson,num);
}
LL query(int L, int R,int l,int r,int rt,int k){//严格比k大的个数
if(l >= L &&R >= r){
LL as = (r-l+1)*treap.query_rankb(root2[rt],k)+treap.query_rankb(root1[rt],k);
return as;
}
LL ans = 0;
if(l <= L && R <= r){
ans = treap.query_rankb(root2[rt],k) *(R-L+1);
}
else if(r < R && r >= L){
ans = treap.query_rankb(root2[rt],k)*(r-L+1);
}
else if(l > L &&l <= R){// shuchu : 3 2
ans = treap.query_rankb(root2[rt],k)*(R-l+1);
// cout<<l<<" ~ "<< r << " ans = "<<ans<<endl;
}
int m= (l+r)>>1;
if(m >= L)
ans+=query(L,R,lson,k);
if(m < R)
ans+=query(L,R,rson,k);
return ans;
}
int bs(int L,int R,LL k){ //二分求答案
int l = 1, r = N ;//cout << " bigger than 0 "<<query(L,R,1,N,1,0)<<endl;
while( l <= r){
//cout <<" l = "<<l<<" r "<< r<<endl;
int mid = (l+r) >>1;
LL cm = query(L,R,1,N,1,mid), cm1 = query(L,R,1,N,1,mid-1);//严格大于mid的个数
if(cm < k && cm1 >= k){
return mid;
}
else if(cm >= k) l = mid+1;
else r = mid-1; //cm<k&&cm1<k
}
}
int op,l,r,k;
void sov(){
treap.cnt = tot = 0;
for(int i =1 ; i <= Q ; i++){
scanf("%d ",&op);
if(op == 1){
scanf("%d%d%d",&l,&r,&k);
if(l>r) swap(l,r);
update(l,r,1,N,1,k);
}
else{
scanf("%d%d%d",&l,&r,&k);
if(l > r) swap(l,r);
printf("%d\n",bs(l,r,k));
}
}
}
int main(){
while(~scanf("%d%d ",&N,&Q)){
build(1,N,1);
sov();
}
return 0;
}
/*
7 100
1 1 5 2
1 2 7 3
1 3 5 2
1 1 1 1
2 1 3 2
2 1 3 3
2 1 3 7
2 1 7 5
2 1 7 7
2 2 3 2
2 1 1 1
2 1 1 2
2 3 5 3
2 3 4 5
*/
怕是有点慢,还有个做法是线段树套线段树,外面是权值,每个节点上的线段树是位置,
比如,如果在1~4位置加入5 就是把整个线段树中有5的log个节点,每个节点的1~4这log区间上++。。。。然后每次询问的时候,二分答案,比如权值二分到3就是求1~3中有多少个在询问的区间里,还是log个节点,每个节点跑log个得到对应区间上的个数总和。。。待补