https://vjudge.net/contest/386618#problem/A
A
题意:
给定一串数,问每一个数最右边的小于这个数和这个数之间的距离是多少,如果没有输出-1.
我的思路:
我是用数状数组来解决的。
struct skr
{ ll v;
int i;
}a[N];
1.先按给定的顺序给每个数一个序号id
for(int i=1;i<=n;i++)
{cin>>a[i].v;a[i].i=i;}
2.将这个数组按照v从小到大排序
3.从左到右遍历排好序的数组(设当前遍历到的数下标为i)
4.add(a[i].i,1)
5.然后记录最右边的位置ma=max(a[i].i,ma)
6.如果sum(n)-sum(a[i].i)!=0的话,说明在这个数的右边有比它小的数,这个数肯定就是ma(因为ma是最靠右)
7.用ma减去a[i].id就可以了
这是我的思路,但是错了,我去codeforce上提交了一次,想看看错误数据,结果对了几千数据,卡在某一个位置出错了,最怕这种情况了。我又想了好长时间,都不知道是怎么错的。我真不知道是哪里错了,去网上找有没有是用树状数组做的,结果找不到,全都是用线段树做的,难道这个题真的不能用树状数组做吗?我觉得可以啊,反正现在是想不出来了,先写下来,等脑子清醒点再想一想吧。
//问题代码:
#include<bits/stdc++.h>
using namespace std;
#define mod 1e9+7
#define N 1000005
#define inf 0x3f3f3f3f
const double PI = atan(1.0)*4.0;
typedef long long ll;
ll b[N],p[N],c[N];
int n;
struct skr
{
ll i,v;
}a[N];
int lowbit(int x)
{
return x&(-x);
}
int add(int pos,int v)
{
while(pos<=n)
{
c[pos]+=v;
pos+=lowbit(pos);
}
}
ll sum(int pos)
{
ll ans=0;
while(pos>=1)
{
ans+=c[pos];
pos-=lowbit(pos);
}
return ans;
}
bool cmp(skr a,skr b)
{
return a.v<b.v;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("E:\\in.txt","r",stdin);
cin>>n;
for(int i=1;i<=n;i++)
{cin>>a[i].v;a[i].i=i;}
sort(a+1,a+1+n,cmp);
ll ma=-1;
for(int i=1;i<=n;i++)
{
add(a[i].i,1);
ma=max(ma,a[i].i);
//cout<<sum(n)<<" "<<sum(a[i].i)<<" "<<sum(n)-sum(a[i].i)<<endl;
if(sum(n)-sum(a[i].i)==0)
b[a[i].i]=-1;
else
{
b[a[i].i]=ma-a[i].i-1;
}
// cout<<"b"<<a[i].i<<":"<<b[a[i].i]<<endl;
}
for(int i=1;i<=n;i++)
cout<<b[i]<<" ";
}
然后就是最普遍的线段树做法
每个节点表示该段的最小值, 循环一遍数组,每个位置找到最靠右且比它小的数后(已经处理过),将其改为INF,并对线段树进行最小值更新。
查询之前, 先询问整个数组的最小值和当前值的比较,满足最小值小于当前值才有查询的必要,如果满足查询的条件,右边不成立就一定在左边。
D
题意:
给出一个长度为 n 的递增集合,现在要在集合中取出 3 个数,使得最大数与最小数的差值小于 d,问有多少种取法
思路:
我们只要求出第一个和第三个的数符合题意,中间有多少数就有多少种情况
对于第i个位置,我们用二分来求满足a[j]-a[i]的最右位置和最左位置,这两个数之间的数都满足题意。为什么要用二分呢?因为如果用双重循环的话,最坏情况是1010超时。
线段树用来求某一段的所有数的个数和
我们用它来求最左边满足题意的数与a[i]之间(不包括边界)的数的个数,利用推到的公式算就好了
#include<bits/stdc++.h>
using namespace std;
#define mod 1e9+7
#define N 100005
#define inf 0x3f3f3f3f
const double PI = atan(1.0)*4.0;
typedef long long ll;
ll b[N],p[N],c[N];
ll n,d;
struct skr
{
ll data;
int l,r;
}a[4*N];
void build(int node,int start,int endd)
{
a[node].l=start,a[node].r=endd;
if(start==endd)
{
a[node].data=1;
return;
}
int mid=(start+endd)>>1;
int L_tree=2*node;
int R_tree=2*node+1;
build(L_tree,start,mid);
build(R_tree,mid+1,endd);
a[node].data=a[L_tree].data+a[R_tree].data;
}
ll qury(int node,int L,int R)
{ ll ans=0;
if(a[node].l>=L&&a[node].r<=R)
{
return a[node].data;
}
if(a[node].r<L||a[node].l>R)
return 0;
int mid=(a[node].l+a[node].r)>>1;
if(a[node*2].r>=L)
ans+=qury(node*2,L,R);
if(a[node*2+1].l<=R)
ans+=qury(node*2+1,L,R);
return ans;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
// freopen("E:\\in.txt","r",stdin);
cin>>n>>d;
for(int i=1;i<=n;i++)
cin>>b[i];
build(1,1,n);
ll sum=0;
for(int i=1;i<=n-2;i++)
{
int l=i+2,r=n,mid,pl=inf,pr=inf,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(b[mid]-b[i]<=d)
{
r=mid-1;
pl=mid;
}
else
r=mid-1;
}
if(pl!=inf)
{
l=i+2,r=n;
while(l<=r)
{
mid=(l+r)>>1;
if(b[mid]-b[i]<=d)
{
l=mid+1;
pr=mid;
}
else
r=mid-1;
}
}
if(pl!=inf)
{
sum+=(pr-pl+2*qury(1,i+1,pl-1))*(pr-pl+1)/2;
}
}
cout<<sum<<endl;
}