2020.01.27日常总结

P 1637      \color{green}{洛谷P1637\ \ \ \ 三元上升子序列}

\color{blue}{【题意】:} Erwin最近对一种叫thair的东西巨感兴趣……

在含有 n n 个整数的序列 a 1 , a 2 . . . a n a_1,a_2...a_n 中,

三个数被称作thair当且仅当 i < j < k i<j<k a i < a j < a k a_i<a_j<a_k

求一个序列中thair的个数。

\color{blue}{【思路】:} 像这种,考虑三个数之间的关系的题目,我们可以从中间元素入手。

L e f t i Left_i 表示第 i i 个数左边小于它的数的个数, R i g h t i Right_i 表示第 i i 个数右边大于它的数的个数。不难发现,总答案 a n s ans 满足:

a n s = i = 1 n L e f t i × R i g h t i ans=\sum\limits_{i=1}^{n}Left_i \times Right_i

既然如此,我们就可以用 \color{red}{树状数组} 来求出 L e f t Left R i g h t Right 。总的时间复杂度为 O ( n × log n ) O(n \times \log n)

需要注意的是,因为 a a 的取值范围太大,所以我们先要对它进行 \color{red}{离散化}

\color{blue}{【代码】:}

const int N=30100;
struct node{
	int sub,num,number;
}a[N];int n,m,c[2][N];
inline bool cmp1(node a,node b){
	return a.num<b.num;
}
inline bool cmp2(node a,node b){
	return a.sub<b.sub;
}
inline int F(int x){
	return x&(-x);
}//相当于树状数组的lowbit函数 
typedef long long ll;
inline void updata(int x,int p){
	for(;x<=m;x+=F(x)) c[p][x]++;
}//树状数组的修改操作 
inline int query(int x,int p){
	register int cnt=0;
	for(;x;x-=F(x))
		cnt+=c[p][x];
	return cnt;
}//树状数组的求前缀和操作 
int Left[N],Right[N];ll ans;
int main(){
	freopen("t1.in","r",stdin);
	scanf("%d",&n);ans=0ll;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i].num);
		a[i].sub=i;//保存该数的下标 
	}
//	从第33行到第40行为离散化操作 
	sort(a+1,a+n+1,cmp1);
	a[1].number=1;
	for(int i=2;i<=n;i++)
		if (a[i].num!=a[i-1].num)
			a[i].number=a[i-1].number+1;
		else a[i].number=a[i-1].number;
	m=a[n].number;
	sort(a+1,a+n+1,cmp2);
	for(int i=1;i<=n;i++){
		updata(a[i].number,0);
		Left[i]=query(a[i].number-1,0);
	}//求Left数组 
	for(int i=n;i;i--){
		updata(m-a[i].number+1,1);
		Right[i]=query(m-a[i].number,1);
	}//求Right数组,通过减法把求大于一个数的数的个数操作转化为了求小于该数的数的个数的操作 
	for(int i=1;i<=n;i++)//计算ans,注意中间结果不要溢出 
		ans+=(ll)Left[i]*Right[i];
	printf("%lld",ans);
	return 0;
}
发布了103 篇原创文章 · 获赞 4 · 访问量 6743

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/104092906
今日推荐