• 三元上升子序列


    题意:
    给定一个长度为n的数列(a_1, a_2, a_3...a_n),三个数可以被称为三元上升子序列,当且仅当(i < j < k)(a_i < a_j < a_k)

    其实只要是三元,不管题目是要求(a_i < a_j < a_k)还是(a_i > a_j > a_k)还是(a_i < a_j > a_K)还是(a_i > a_j < a_k)还是要求可以取等号,都可用同一思路做的啦。
    大概就是我们以中间那个数(即(a_j))为单位,分别统计在它前面比它小的,和在它后面比它大的,然后乘一下就得到以(a_j)为中间那个数的三元上升子序列个数。于是我们枚举每一个数作为中间数考虑,然后把得到的数累加就可以得到答案了。
    为了降低复杂度,我们先从前往后枚举一遍,依次把数加入树状数组,加入它之前就查询一下当前树状数组中比它小的有几个。(这里如果不能取等的话,在加入之前还是之后查询都无所谓,如果可以取等号,就要在加入之前查询)
    然后再从后往前枚举一遍,同样依次把数加入树状数组,加入每个数之前就查询一下当前树状数组中比它大的有几个。
    然后对于每个数所记录的两个数据相乘再累加即可。

    这里的两次枚举,就是为了保证数字下标之间的大小关系,而树状数组则是用来保证数字大小之间的数量关系,明确这一点之后思路应该就很清晰了。
    代码的话emmm……下次再补吧

    2020.9.22 update:

    #include<bits/stdc++.h>
    using namespace std;
    #define Ri register int
    #define AC 60000
    #define lowbit(x) (x & (-x))
    #define LL long long
    
    int n; LL ans;
    int tree[AC], be[AC], af[AC];
    struct node{int id, num, w;}s[AC];
    
    inline bool cmp(node a, node b) {return a.num < b.num;}
    inline bool cmp2(node a, node b) {return a.id < b.id;}
    
    inline int read()
    {
    	int x = 0; char ch = getchar(); bool z = false;
    	while((ch > '9' || ch < '0') && ch != '-') ch = getchar();
    	if(ch == '-') z = true, ch = getchar();
    	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    	return z ? -x : x;
    }
    
    void pre()
    {
    	n = read();
    	for(Ri i = 1; i <= n; i ++) s[i].id = i, s[i].num = read();
    	sort(s + 1, s + n + 1, cmp);
    	int now = 0;
    	for(Ri i = 1; i <= n; i ++) 
    	{
    		++ now, s[i].w = now;
    		while(s[i].num == s[i + 1].num) s[++i].w = now;
    	}
    	sort(s + 1, s + n + 1, cmp2);
    }
    
    int cnt(int x)
    {
    	int now = 0;
    	for(Ri i = x; i; i -= lowbit(i)) now += tree[i];
    	return now;
    }
    
    void add(int x) 
    {
    	if(!x) return ;
    	for(Ri i = x; i <= n; i += lowbit(i)) tree[i] ++;
    }
    
    void clear(){for(Ri i = 1; i <= n; i ++) tree[i] = 0;}
    
    void work()
    {
    	for(Ri i = 1; i <= n; i ++) be[i] = cnt(s[i].w - 1), add(s[i].w);
    	clear();
    	for(Ri i = n; i; i --) af[i] = n - i - cnt(s[i].w), add(s[i].w);
    	for(Ri i = 1; i <= n; i ++) ans += 1LL* be[i] * af[i]; 
    	printf("%lld
    ", ans);
    	/*for(Ri i = 1; i <= n; i ++) printf("%d ", be[i]);
    	printf("
    ");
    	for(Ri i = 1; i <= n; i ++) printf("%d ", af[i]);
    	printf("
    ");*/
    }
    
    int main()
    {
    //	freopen("1.in", "r", stdin);
    	pre();	
    	work();
    //	fclose(stdin);
    	return 0;
    }
    

    写了离散化,后面调用的时候用了原数值……然后成功wa了两次,老年退役且痴呆选手好难

  • 相关阅读:
    形近词辨析
    anger,fury,rage,indignation,ire区别
    熟词僻义
    fate,destiny,luck,doom区别
    whk 学习笔记
    常见词根
    个人简介
    CF883D Packmen Strike Back
    CF859E Desk Disorder
    CF140E New Year Garland
  • 原文地址:https://www.cnblogs.com/ww3113306/p/13655009.html
Copyright © 2020-2023  润新知