动态逆序对(删除数)--洛谷P1393(离散化+分块+树状数组)&洛谷P3157

题目链接https://www.luogu.org/problem/P1393

题目描述

对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i<j的有序对(i,j)的个数。你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数。

输入格式

第一行,两个数n,m,表示序列中有n个数,要删去m个数

第二行n个数,表示给定的序列。

第三行m个数,第i个数di表示要删去原序列中的第di个数。

输出格式

一行m+1个数。第一个数表示给定序列的逆序对组数,第i+1个数表示删去第di个数后序列的逆序对组数(删去的数不再恢复)

输入输出样例

输入 

6 3
5 4 2 6 3 1
2 1 4

输出

11 7 4 2

说明/提示

对于20%的数据,n≤2500

对于另30%的数据,m=0

对于100%的数据,n≤40000,m≤n/2,且保证第二行n个数互不相同,第三行m个数互不相同。


这题题目有点问题,它的a[i]的范围没给你,导致了一发RE。。。所以我们需要对a[i]离散化,又由于他的a[i]是相互不同的,那么我们sort完之后用unordered_map离散化就好了:

for (int i=1; i<=n; i++) {
	in(a[i].val);//快读
	b[i]=a[i];
}
sort(b+1,b+1+n,cmp);
for (int i=1; i<=n; i++) q[b[i].val]=i;

接下来就是暴力分块了,首先我们要清楚的是,删去了这个数之后,他会的逆序对贡献会减少前面比他大的数的个数,再减少后面比他小的数的个数,比如5 4 3 2 1  我们删去了3,那么前面比他大的数的个数,也就是2(5和4比他大),后面比他小的数的个数为2(2和1)

所以我们就直接对每个块排序,对于包含位置x的块直接暴力数:

if (loca==i){
	for (int j=L[i]; j<=R[i]; j++) {
		if (a[j].pos==pos) {
			a[j].val=inf;
			continue;
		}
		if ((a[j].pos<pos && a[j].val>val) || (a[j].pos>pos && a[j].val<val)) ans--;
	}
	sort(a+L[i],a+R[i]+1,cmp);
	R[i]--;//这个块中元素删去了一个,缩小块
}

对于loca前面的块,我们找有多少个比他大的数的个数,也就是二分查找:

if (i<loca) {
	int nb=R[i]-L[i]+1;
	int it=nb-(lower_bound(a+L[i],a+R[i]+1,node {pos,val})-(a+L[i]));
	ans-=it;
}

后面的块也是一样的。

当然,我们对我们需要对块进行初始化,先排好序就好了

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=1e5+10;
const int inf=1e9+10;

#define ll long long

int L[1000],R[1000],id[mac];
int tree[mac],mp[mac],t,n;
ll ans=0;
struct node
{
    int pos,val;
    bool operator<(const node &a)const{
        return val<a.val;
    }
}a[mac],b[mac];

int lowbit(int x){return x&-x;}

int query(int pos)
{
    int sum=0;
    while (pos){
        sum+=tree[pos];
        pos-=lowbit(pos);
    }
    return sum;
}

void update(int pos,int val)
{
    while (pos<=n){
        tree[pos]+=val;
        pos+=lowbit(pos);
    }
}

bool cmp(node a,node b){return a.val<b.val;}
bool cmp2(const node &a,const node &b) {return a.val<b.val;}

void solve(int pos,int val)
{
    int loca=id[pos];
    for (int i=1; i<=t; i++){
    	if (L[i]>R[i]) continue;
        if (i<loca){
            int nb=R[i]-L[i]+1;
            int it=nb-(lower_bound(a+L[i],a+R[i]+1,node{pos,val})-(a+L[i]));
            ans-=it;
        }
        else if (i>loca){
            int it=lower_bound(a+L[i],a+R[i]+1,node{pos,val})-(a+L[i]);
            ans-=it;
        }
        else{
            for (int j=L[i]; j<=R[i]; j++){
                if (a[j].pos==pos){a[j].val=inf;continue;}
                if ((a[j].pos<pos && a[j].val>val) || (a[j].pos>pos && a[j].val<val)) ans--;
            }
            sort(a+L[i],a+R[i]+1,cmp);
            R[i]--;
        }
    }
}

void in(int &x)
{
	int f=0;
	char ch=getchar();
	while (ch>'9' || ch<'0') ch=getchar();
	while (ch<='9' && ch>='0') f=(f<<3)+(f<<1)+ch-'0',ch=getchar();
	x=f;
}

void out(ll x)
{
	if (x>=10)
		out(x/10);
	putchar(x%10+'0');
}

unordered_map<int,int>q;

int main()
{
    //freopen("in.txt","r",stdin);
	int m;
    in(n);in(m);
    t=sqrt(n);
    for (int i=1; i<=t; i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if (R[t]<n) t++,L[t]=R[t-1]+1,R[t]=n;
    for (int  i=1; i<=t; i++)
        for (int j=L[i]; j<=R[i]; j++)
            id[j]=i;
    ans=0;
    for (int i=1; i<=n; i++){
        in(a[i].val);
        b[i]=a[i];
    }
    sort(b+1,b+1+n,cmp);
    for (int i=1; i<=n; i++) q[b[i].val]=i;
    for (int i=1; i<=n; i++) {
        mp[i]=q[a[i].val];a[i].pos=i;
        ans+=i-1-query(q[a[i].val]);
        update(q[a[i].val],1);
    }
    for (int i=1; i<=t; i++){
        sort(a+L[i],a+R[i]+1,cmp);
        for (int j=L[i]; j<=R[i]; j++) a[j].val=q[a[j].val];
    }
    out(ans);
    for (int i=1; i<=m; i++){
        int x;
        in(x);
        solve(x,mp[x]);	
        putchar(' ');
        out(ans);
    }
    putchar('\n');
    return 0;
}

还有一题类似的,洛谷P3157,这题的数据比较毒瘤,要开O2优化(可以手动开O2)才能过

题目链接https://www.luogu.org/problem/P3157

方法和上面是一样的,删去离散化就可以了:

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <bits/stdc++.h>
using namespace std;

const int mac=1e5+10;
const int inf=1e9+10;

#define ll long long

int L[1000],R[1000],id[mac];
int tree[mac],mp[mac],t,n;
ll ans=0;
struct node
{
    int pos,val;
    bool operator<(const node &a)const{
        return val<a.val;
    }
}a[mac];

int lowbit(int x){return x&-x;}

int query(int pos)
{
    int sum=0;
    while (pos){
        sum+=tree[pos];
        pos-=lowbit(pos);
    }
    return sum;
}

void update(int pos,int val)
{
    while (pos<=n){
        tree[pos]+=val;
        pos+=lowbit(pos);
    }
}

bool cmp(node a,node b){return a.val<b.val;}
bool cmp2(const node &a,const node &b) {return a.val<b.val;}

void solve(int pos,int val)
{
    int loca=id[pos];
    for (int i=1; i<=t; i++){
    	if (L[i]>R[i]) continue;
        if (i<loca){
            int nb=R[i]-L[i]+1;
            int it=nb-(lower_bound(a+L[i],a+R[i]+1,node{pos,val})-(a+L[i]));
            ans-=it;
        }
        else if (i>loca){
            int it=lower_bound(a+L[i],a+R[i]+1,node{pos,val})-(a+L[i]);
            ans-=it;
        }
        else{
            for (int j=L[i]; j<=R[i]; j++){
                if (a[j].pos==pos){a[j].val=inf;continue;}
                if ((a[j].pos<pos && a[j].val>val) || (a[j].pos>pos && a[j].val<val)) ans--;
            }
            sort(a+L[i],a+R[i]+1,cmp);
            R[i]--;
        }
    }
}

void in(int &x)
{
	int f=0;
	char ch=getchar();
	while (ch>'9' || ch<'0') ch=getchar();
	while (ch<='9' && ch>='0') f=(f<<3)+(f<<1)+ch-'0',ch=getchar();
	x=f;
}

void out(ll x)
{
	if (x>=10)
		out(x/10);
	putchar(x%10+'0');
}

int main()
{
    //freopen("in.txt","r",stdin);
	int m;
    in(n);in(m);
    t=sqrt(n);
    for (int i=1; i<=t; i++){
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if (R[t]<n) t++,L[t]=R[t-1]+1,R[t]=n;
    for (int  i=1; i<=t; i++)
        for (int j=L[i]; j<=R[i]; j++)
            id[j]=i;
    ans=0;
    for (int i=1; i<=n; i++){
        in(a[i].val);
        mp[a[i].val]=i;a[i].pos=i;
        ans+=i-1-query(a[i].val);
        update(a[i].val,1);
    }
    for (int i=1; i<=t; i++)
        sort(a+L[i],a+R[i]+1,cmp);
    for (int i=1; i<=m; i++){
        int x;
        in(x);
        out(ans);
        putchar('\n');
        solve(mp[x],x);		
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值