题意:
给你n个人和一个k
问你把这n个人分成多少个连续的子区间,要求区间每个数两两相差绝对值小于k
思路:
我们仅仅只需要对于当前位置,最左边那个和它绝对值相差大于等于k 的位置在哪
假设对于i这个位置,最左边的位置是tep,不存在的话tep=0
那么当且位置的贡献就是 sum[i]=min(i-tep,sum[i-1]+1);
那么对于这个位置怎么求的话,我是使用了两个单调队列
第一次维护递增的,第二次维护递减的。
每次先把不满足的点全部出队,然后记录最大的下标
然后入队
代码:
#include"stdio.h" #include"algorithm" #include"string.h" #include"iostream" #include"queue" #include"map" #include"vector" #include"string" #include"cmath" using namespace std; #define N 222222 #define ll __int64 struct node { int x,s; }; node q[N],v[N]; ll sum[N]; int finde_max(int l,int r,int k) { int ans=-1; while(l<=r) { int mid=(l+r)/2; if(q[mid].s>k) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int finde_min(int l,int r,int k) { int ans=-1; while(l<=r) { int mid=(l+r)/2; if(q[mid].s<k) { ans=mid; l=mid+1; } else r=mid-1; } return ans; } int main() { int t; cin>>t; while(t--) { int n,k; scanf("%d%d",&n,&k); for(int i=1; i<=n; i++) { scanf("%d",&v[i].s); v[i].x=i; } int head=0,ed=-1; memset(sum,0,sizeof(sum)); ll ans=0; for(int i=1; i<=n; i++) { int tep=0; if(head<=ed) { while(abs(q[head].s-v[i].s)>=k && head<=ed) { tep=max(tep,q[head].x); head++; } } sum[i]=tep; if(head>ed) q[++ed]=v[i]; else { int kx=finde_min(head,ed,v[i].s); if(kx==-1) q[ed=head]=v[i]; else q[ed=kx+1]=v[i]; } } head=0,ed=-1; for(int i=1; i<=n; i++) { int tep=0; if(head<=ed) { while(abs(q[head].s-v[i].s)>=k && head<=ed) { tep=max(tep,q[head].x); head++; } } sum[i]=max(sum[i],(ll)tep); if(head>ed) q[++ed]=v[i]; else { int kx=finde_max(head,ed,v[i].s); if(kx==-1) q[ed=head]=v[i]; else q[ed=kx+1]=v[i]; } } ll haha=0; for(int i=1; i<=n; i++) { haha=min(haha+1,i-sum[i]); ans+=haha; } printf("%I64d\n",ans); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-14 05:15:54