方法1:暴力模拟(n^2),得分35(20个点)
#include <cstdio>
#include <iostream>
using namespace std;
int a[1999999],s[1999999];
int tot1=0,tot2=0;
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
void get_c()
{
if(tot2==0) return;
int d;
while((d=gcd(tot1,tot2))!=1)
tot1/=d,tot2/=d;
}
int main()
{
freopen("jian.in","r",stdin);
freopen("jian.out","w",stdout);
int n,l,r;
scanf("%d%d%d",&n,&l,&r);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
tot1++;
int len=(j-i+1);
double c=(double)(s[j]-s[i-1])/(double) len;
if(c>=l&&c<=r) tot2++;
}
//tot1=10,tot2=2;
if(tot2%tot1==0)
printf("%d",tot2/tot1);
else
{
get_c();
printf("%d/%d",tot2,tot1);
}
return 0;
}
正解:树状数组+离散化(逃)
这道题居然可以变形为逆序对问题!
证明如下
我们可以分别求平均值在 1-r和1-l(不包括l)的区间个数,前面的减去后面的就是我们要找的区间个数啦!
下面以求1-r为例
(a[i]+a[i+1]+…+a[i+k-1])/k<=r
(a[i]+a[i+1]+…+a[i+k-1])<=r*k
(a[i]+a[i+1]+…+a[i+k-1])-r*k<=0
(a[i]-r+a[i+1]-r+…+a[i+k-1]-r)<=0
令数组c[i]=a[i]-r
上面的式子变为
c[i]+c[i+1]+…+c[i+k-1]<=0
令s[i]为c数组的前缀和
s[i+k-1]-s[i]<=0
s[i+k-1]<=s[i]
又发现
i < i+k-1
这就是逆序对啊QAQ
那么问题就变为求,s数组的逆序对个数。
等一下,s[i]它有可能为负数啊。原来学过的逆序对的方法求不了啊QAQ
题目上也没给a[i]的范围,我咋确定上界啊!
解决办法1:胡乱搞个上届,反正我是欧洲人(逃)
解决办法2:离散化(我TM也不知道这算不算离散化)
我们首先将s[i]的具体值存在另一个数组s2[i]中。
然后将s[i]排序,然后去重函数把相同的去掉(为了后来的上界尽量小)
然后用s2[i]在排过序的s[i]中找第一个大于s2[i]的位置,用返回的下标作为s2[i]的新数字,这样s2[i]中的大小关系与原s数组的大小关系是一样的
所以这两个数组逆序对的个数是相同的,而且由于S2[i]中的存的是下标
,s2[i]数组中的数字相比开始的是变小了,上界可以确定啦!
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxm=500010;
ll r1[maxm],l1[maxm];
ll r2[maxm],l2[maxm];
ll tree[maxm];
ll tot1=0,tot2=0;
ll lowbit(ll x)
{
return x&-x;
}
void add(ll x)
{
for(ll i=x;i<=maxm;i+=lowbit(i))
tree[i]++;
}
ll ask(ll x)
{
ll ans=0;
for(ll i=x;i>=1;i-=lowbit(i))
ans+=tree[i];
return ans;
}
ll gcd(ll a,ll b)
{
if(!b) return a;
return gcd(b,a%b);
}
void get_c()
{
if(tot2==0) return;
ll d;
while((d=gcd(tot1,tot2))!=1)
tot1/=d,tot2/=d;
}
int main()
{
freopen("jian.in","r",stdin);
freopen("jian.out","w",stdout);
ll n,l,r;
scanf("%lld%lld%lld",&n,&l,&r);
for(ll i=1;i<=n;i++)
{
ll x;
scanf("%lld",&x);
r1[i]=r1[i-1]+(x-r);
r2[i]=r1[i];
if(r1[i]<=0) tot1++;
l1[i]=l1[i-1]+(x-l);
l2[i]=l1[i];
if(l1[i]<0) tot2++;
}
sort(r1+1,r1+n+1);
sort(l1+1,l1+n+1);
ll t1=unique(r1+1,r1+n+1)-r1-1;//去重函数,去完重后数组大小变化
ll t2=unique(l1+1,l1+n+1)-l1-1;
for(ll i=1;i<=n;i++)
{
ll pos1=lower_bound(r1+1,r1+t1+1,r2[i])-r1;
r2[i]=pos1;
ll pos2=lower_bound(l1+1,l1+t2+1,l2[i])-l1;
l2[i]=pos2;
}
for(ll i=n;i>=1;i--)
{
tot1+=ask(r2[i]);
add(r2[i]);
}
memset(tree,0,sizeof(tree));
for(ll i=n;i>=1;i--)
{
tot2+=ask(l2[i]);
add(l2[i]+1);
}
tot2=tot1-tot2;
tot1=n*(n+1)/2;
if(tot2%tot1==0)
printf("%lld",tot2/tot1);
else
{
get_c();
printf("%lld/%lld",tot2,tot1);
}
return 0;
}