Description
给出一个长度为
n
的序列,称一对区间
Input
第一行输入一整数
n
,之后输入
Output
输出满足条件的区间对数
Sample Input
4
1 4 2 4
Sample Output
9
Solution
首先找到所有的幸运数字,假设共
m
个,位置为
从
n
个位置中取出两个不相交区间的方案数为
枚举第二个区间包含第
i
个幸运数字到第
假设我们找到两个相邻的不合法位置 L,R 满足 L<posk<R ,考虑此时产生的不合法答案数量,当第一个区间左端点介于 (L,posk] 之间,第一个左区间右端点介于 [posk,R) 之间时,第二个区间左端点介于 (posi−1,posi] 时,第二个区间右端点介于 [posj,posj+1) 之间时不合法,方案数 (posk−L)⋅(R−posk)⋅(posi−posi−1)⋅(posj+1−posj)
假设
posk
是最大的不合法位置,设其前一个位置是
L
,那么产生的不合法方案即为,第一个区间左端点介于
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int n,a[100005],b[maxn],pos[maxn],pre[maxn];
set<int>s;
set<int>::iterator l,r;
bool check(int n)
{
while(n)
{
if(n%10!=4&&n%10!=7)return 0;
n/=10;
}
return 1;
}
ull C(int n,int m)
{
if(m==2)return (ull)n*(n-1)/2;
if(m==3)return (ull)n*(n-1)*(n-2)/6;
if(m==4)return (ull)n*(n-1)*(n-2)/6*(n-3)/4;
}
int main()
{
while(~scanf("%d",&n))
{
int m=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(check(a[i]))pos[++m]=i;
}
pos[m+1]=n+1;
for(int i=1;i<=m;i++)
{
pre[i]=0;
for(int j=i-1;j>=1;j--)
if(a[pos[i]]==a[pos[j]])
{
pre[i]=j;
break;
}
}
ull ans=C(n,4)+2ull*C(n,3)+C(n,2);
for(int i=2;i<=m;i++)
{
s.clear();
s.insert(0);
ull res=0;
for(int j=i;j<=m;j++)
{
if(pre[j]<i)
{
for(int k=pre[j];k;k=pre[k])
{
l=r=s.lower_bound(pos[k]);
l--;
if(r==s.end())
{
res+=(ull)(pos[k]-*l)*(pos[i-1]-pos[k])*(pos[i]-pos[i-1]);
res+=(ull)(pos[k]-*l)*C(pos[i]-pos[i-1]+1,2);
}
else res+=(ull)(pos[k]-*l)*(*r-pos[k])*(pos[i]-pos[i-1]);
s.insert(pos[k]);
}
}
ans-=res*(pos[j+1]-pos[j]);
}
}
printf("%I64u\n",ans);
}
return 0;
}