Hello 2020
B New Year and Ascent Sequence
-
题意:一个数字序列 S 中如果存在(S_{i}<S_{j} (i<j)),就说它有“ ascent ” 。在N组数字序列中两两匹配,看有多少组序列组合存在“ ascent ” 。
-
分析:
- 两两组合有n2种情况
- 先不考虑组合中某一序列本身有“ ascent ”的情况。这样当我们处理序列S1时,只要其他序列中有比S1序列的最小值大的数,他们就能构成S1+Si的序列,这个序列满足条件。方便判断,只要Si序列中最大值大于S1序列的最小值就行。
- 所以我们清楚了要记录所有序列的最大值,最小值。然后维护n个序列最大值中大于当前序列最小值的个数,这也就是当前判断的序列对结果的贡献。见代码。
- 考虑某序列自身就有“ ascent ”的情况。那它和其他任何序列都可以组合。对于特殊情况应该想到要么对特例进行特殊处理。但在这道题中这么做有点麻烦。还要想到能不能让特殊情况适合一般的情况的解法中。分析后发现,一个序列和其他任何序列都可以组合满足条件。那么它就不受最大值最小值影响。我们把它的最小值改为最小的一个值,最大值改为最大的一个值。这样其他序列的最小值一定比他的最大值小,其他序列可以和他组合。它的最小值一定比其他序列的最大值小,它可以和其他序列组合。这样就将做法统一了。
- 注意: 特殊情况的最大值最小值不要设为其他序列最值边界值.
-
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MA=1e6+15; const int MB=1e5+5; const int INF=1e9+7; ll n,l; ll minn[MB],maxx[MB]; //记录每个序列最小值,最大值 ll dmax[MA]; // ll ans=0,ax; int flag[MB]; //自身是否满足 int main() { scanf("%lld",&n); for(int i=1;i<=n;++i){ scanf("%lld",&l); minn[i]=INF; maxx[i]=0; for(int j=1;j<=l;++j){ scanf("%lld",&ax); if(ax>minn[i])flag[i]=1; minn[i]=min(minn[i],ax); maxx[i]=max(maxx[i],ax); } minn[i]=minn[i]+1; //调整最值区间 maxx[i]=maxx[i]+1; if(flag[i]){ //如果序列本身满足条件,修改最值 minn[i]=0; maxx[i]=1e6+5; } dmax[maxx[i]]++; } ll val1=0,val2=0; for(int i=1e6+10;i>=0;--i){ val2=dmax[i]; dmax[i]=val1; val1+=val2; } for(int i=1;i<=n;++i){ ans+=dmax[minn[i]]; } printf("%lld ",ans); return 0; }