(最近突然无聊,决定给大家秀一下我的图片库)

原题链接:
hdu
题意简述
求一个序列(保证是 [ 1 , n ] [1,n] [1,n]的一个排列, n < = 1 e 5 n<=1e5 n<=1e5)中有多少个三元组 ( i , j , k ) (i,j,k) (i,j,k)满足 i < j < k i<j<k i<j<k且 ( a [ i ] < a [ k ] < a [ j ] ) (a[i]<a[k]<a[j]) (a[i]<a[k]<a[j])?
数据
输入
多组(确定组数)。每组:
n
a1 a2 ... an//读入序列
输出
Case #1:ans
Case #2:ans
...
Case #t:ans//每组输出答案,膜1e9+7
样例
输入
2
6
1 3 2 6 5 4
5
3 5 2 4 1
输出
Case #1: 10
Case #2: 1
思路
(请先保证您会用权值树状数组求出三元上升子序列,也就是把这个题的条件改为 a [ i ] < a [ j ] < a [ k ] a[i]<a[j]<a[k] a[i]<a[j]<a[k]这样的问题,然后看下去)
我们发现 i i i不仅是下标最小,也是值最小,考虑枚举这个 i i i。
然后就是要在
i
i
i后面找有多少
j
,
k
j,k
j,k满足
j
<
k
j<k
j<k且
a
[
j
]
>
a
[
k
]
a[j]>a[k]
a[j]>a[k]且
a
[
j
]
a[j]
a[j]和
a
[
k
]
a[k]
a[k]都
>
a
[
i
]
>a[i]
>a[i]。
.假设我们此时不考虑
a
[
j
]
>
a
[
k
]
a[j]>a[k]
a[j]>a[k],那就好做很多了。我们在
[
i
+
1
,
n
]
[i+1,n]
[i+1,n]中比
a
[
i
]
a[i]
a[i]大的数里面随便选两个
(
j
,
k
)
(j,k)
(j,k)满足
j
<
k
j<k
j<k,且
a
[
j
]
,
a
[
k
]
>
a
[
i
]
a[j],a[k]>a[i]
a[j],a[k]>a[i]。
设
[
i
+
1
,
n
]
[i+1,n]
[i+1,n]中有
R
R
R个比
i
i
i大,那么此时的答案就是
C
R
2
=
(
R
∗
(
R
−
1
)
/
2
)
C_R^2=(R*(R-1)/2)
CR2=(R∗(R−1)/2)。
珂是这样多算了哪些答案呢?就是不满足
a
[
j
]
>
a
[
k
]
a[j]>a[k]
a[j]>a[k]的答案(理论上
a
a
a不大于
b
b
b是
a
<
=
b
a<=b
a<=b,但是排列里面不会取等,所以这里不满足
a
[
j
]
>
a
[
k
]
a[j]>a[k]
a[j]>a[k]就是
a
[
j
]
<
a
[
k
]
a[j]<a[k]
a[j]<a[k])。
然后这样子的答案,我们现在是没法求的,但是珂以在以后去除掉。而现在要考虑的,是把以前的去除掉。就是要在找一个
i
′
i'
i′,在右边找一个
j
j
j,使得
a
[
i
]
>
a
[
i
′
]
a[i]>a[i']
a[i]>a[i′],
a
[
j
]
>
a
[
i
′
]
a[j]>a[i']
a[j]>a[i′],但是
a
[
i
]
<
a
[
j
]
a[i]<a[j]
a[i]<a[j],把这样的情况给去掉。这个自然是很好找的,就是"三元上升子序列"的做法了。
设左边有
L
L
L个比
a
[
i
]
a[i]
a[i]小,右边有
R
R
R个比
a
[
i
]
a[i]
a[i]大,情况数就是
L
∗
R
L*R
L∗R。
(最后说一下,其实我们珂以由“
左
边
<
a
[
i
]
左边<a[i]
左边<a[i]”的情况数推算出“
右
边
>
a
[
i
]
右边>a[i]
右边>a[i]”的情况数的。
设“
左
边
<
a
[
i
]
左边<a[i]
左边<a[i]”有
L
L
L个。
那么
左
边
>
a
[
i
]
左边>a[i]
左边>a[i]有
(
i
−
1
)
−
L
(i-1)-L
(i−1)−L个。
一共有
n
−
a
[
i
]
n-a[i]
n−a[i]个数比
a
[
i
]
a[i]
a[i]大。
右
边
>
a
[
i
]
右边>a[i]
右边>a[i]有
总
共
的
−
左
边
>
a
[
i
]
=
(
n
−
a
[
i
]
)
−
(
i
−
1
−
L
)
总共的-左边>a[i]=(n-a[i])-(i-1-L)
总共的−左边>a[i]=(n−a[i])−(i−1−L)个。
然后就求出来了。。。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 200100
#define mod 100000007ll
#define MEM(x,a) memset(x,a,sizeof(x))
#define CLS(x) MEM(x,0)
#define int long long
class BIT
{
public:
int tree[N];
int len;
void BuildTree(int _len)
{
len=_len;
CLS(tree);
}
void Add(int pos,int x)
{
while(pos<=len)
{
tree[pos]+=x;
pos+=(pos&(-pos));
}
}
int Query(int pos)
{
int ans=0;
while(pos>0)
{
ans+=tree[pos];
pos-=(pos&(-pos));
}
return ans;
}
}T;
int n,a[N];
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n);
for(int i=1;i<=n;++i)
{
R1(a[i]);
}
}
void Soviet()
{
T.BuildTree(n);
int ans=0;
for(int i=1;i<=n;++i)
{
T.Add(a[i],1);
int L=T.Query(a[i]-1);//左边有L个比a[i]小
int R=(n-a[i])-(i-1-L);//右边有R个比a[i]大
ans+=R*(R-1)/2-L*R;//一个计算当前的答案,一个去掉之前多算的答案
}
printf("%lld\n",ans%mod);
}
void IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
int t;R1(t);
for(int i=1;i<=t;++i)
{
Input();
printf("Case #%lld: ",i);
Soviet();
}
}
#undef int//long long
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
本文解析了一个关于寻找特定三元组的问题,利用权值树状数组进行高效计算,并提供了一种新颖的方法来解决更复杂的条件。通过枚举最小元素并结合组合数学技巧,有效地避免了重复计算。
737

被折叠的 条评论
为什么被折叠?



