洛谷2448 无尽的生命(树状数组)

题目描述

逝者如斯夫,不舍昼夜!
叶良辰认为,他的寿命是无限长的,而且每天都会进步。
叶良辰的生命的第一天,他有1点能力值。第二天,有2点。第n天,就有n点。也就是S[i]=i
但是调皮的小A使用时光机,告诉他第x天和第y天,就可以任意交换某两天的能力值。即S[x]<-->S[y]
小A玩啊玩,终于玩腻了。
叶良辰:小A你给我等着,我有100种办法让你生不如死。除非能在1秒钟之内告知有多少对“异常对”。也就是说,最后的能力值序列,有多少对的两天x,y,其中x<y,但是能力值S[x]>S[y]?
小A:我好怕怕啊。
于是找到了你。

输入输出格式

输入格式:

第一行一个整数k,表示小A玩了多少次时光机
接下来k行,x_i,y_i,表示将S[x_i]与S[y_i]进行交换

输出格式:

有多少“异常对”

输入输出样例

输入样例#1:
2
4 2
1 4
输出样例#1:
4

说明

样例说明
最开始是1 2 3 4 5 6...
然后是  1 4 3 2 5 6...
然后是  2 4 3 1 5 6...
符合的对是[1 4] [2 3] [2 4] [3 4]

对于30%的数据,x_i,y_i <= 2000
对于70%的数据, x_i,y_i <= 100000
对于100%的数据, x_i.y_i <= 2^31-1 k<=100000


思路:树状数组求逆序对。但是数据太大,直接做会超。于是,发现找区间的逆序对和点的逆序对之和就是所要求的答案。离散化一下,1、统计当前i(离散后的值)和i+1(同)的区间长度x*sum[ i ](i之后小于等于i的数),然后更新sum[ i ](+x),2、再统计点的逆序对(正常求法)。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=2*100005;
ll ans=0,ha[maxn],q1[maxn],q2[maxn],a[maxn],sum[maxn];
int n,p[maxn],t=0,cnt=0;
inline int get(){
    char c;while(!isdigit(c=getchar()));
    int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
    return v;
}
inline int getpos(int x){
    int l=1,r=cnt;
    while(l<r){
	    int mid=(l+r)>>1;
	    if(x<=ha[mid])r=mid;
	    else l=mid+1;
	}
	return l;
}
inline int lowbit(int x){return (x&(-x));}
inline void add(int x,ll y){
    while(x<=cnt){
	    sum[x]+=y;
	    x+=lowbit(x);
	}
}
inline ll getsum(int x){
    ll ret=0;
    while(x){
	    ret+=sum[x];
	    x-=lowbit(x);
	}
	return ret;
}
int main(){
    n=get();memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;++i)a[++t]=q1[i]=get(),a[++t]=q2[i]=get();
    sort(a+1,a+1+t);
    for(int i=1;i<=t;++i)if(a[i]!=a[i-1])ha[++cnt]=a[i];
    for(int i=1;i<=cnt;++i)p[i]=i;
    for(int i=1;i<=n;++i){
	    int x=getpos(q1[i]),y=getpos(q2[i]);
	    int tt=p[x];p[x]=p[y];p[y]=tt;
	}
	add(p[cnt],1);
	for(int i=cnt-1;i>=1;--i){
	    ll x=(ha[i+1]-ha[i]-1),y=getsum(i);
	    ans+=x*y;
	    add(i,x);
	    ans+=getsum(p[i]-1);
	    add(p[i],1);
	}
	printf("%lld\n",ans);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值