[洛谷P5677][题解][GZOI2017]配对统计

先上题

这道题乍一看毫无头绪,其实找到了切入点就很简单了

我们发现,所有可用的配对是可以预处理出来的!!!(显然)

于是我们可以排一遍序,统计一个数两边的数,然后把较小的配对记录下来

注意:如果左右相等需要都记录,必须是两个if,不能顺手打成else if(具体看代码)

那么询问如何处理呢?

我们显然不能一个个暴力统计,于是就想到了一个类似莫队的方法:
按左端点排序,挨个向左加

然后我们就可以很快看题解想到一个求答案的方法:

开一个树状数组维护右端点出现的次数(这里的树状数组起到了类似桶的作用)

由于l是下降的,所以已统计过的r绝对不会飞到l左边导致重复统计

这时只需要求一下1~r的前缀和即可

Code:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cmath>
  4 #include<ctime>
  5 #include<cstring>
  6 #include<iostream>
  7 #include<algorithm>
  8 #include<stack>
  9 #include<queue>
 10 #include<vector>
 11 #include<bitset>
 12 #include<set>
 13 #include<map>
 14 #define LL long long
 15 #define rg register
 16 #define us unsigned
 17 #define eps 1e-6
 18 #define INF 0x3f3f3f3f
 19 #define ls k<<1
 20 #define rs k<<1|1
 21 #define tmid ((tr[k].l+tr[k].r)>>1)
 22 #define nmid ((l+r)>>1)
 23 #define Thispoint tr[k].l==tr[k].r
 24 #define pushup tr[k].wei=tr[ls].wei+tr[rs].wei
 25 #define pub push_back
 26 #define lth length
 27 #define int long long
 28 using namespace std;
 29 inline void Read(int &x){
 30     int f=1;
 31     char c=getchar();
 32     x=0;
 33     while(c<'0'||c>'9'){
 34         if(c=='-')f=-1;
 35         c=getchar();
 36     }
 37     while(c>='0'&&c<='9'){
 38         x=(x<<3)+(x<<1)+c-'0';
 39         c=getchar();
 40     }
 41     x*=f;
 42 }
 43 #define N 300010
 44 int n,m,t[N],ans[N],tot;
 45 
 46 struct Num {//原数组存值和位置 
 47     int num,idx; 
 48     bool operator < (const Num a)const{
 49         return num<a.num;
 50     }
 51 }a[N];
 52 
 53 struct Couple {//配对数组存各自位置 
 54     int l,r;
 55     bool operator < (const Couple a)const{
 56         return l<a.l;
 57     }
 58 }c[N];
 59 
 60 struct Question {//询问数组存区间和排名(输出用) 
 61     int l,r,idx;
 62     bool operator < (const Question a)const{
 63         return l<a.l;
 64     }
 65 }q[N];
 66 /*---以下为树状数组基本操作---*/
 67 inline int lbt(int x){
 68     return x&(-x);
 69 }
 70 inline void add(int x,int num){
 71     while(x<=n){
 72         t[x]+=num;
 73         x+=lbt(x);
 74     }
 75 }
 76 inline int query(int x){
 77     int ans=0;
 78     while(x){
 79         ans+=t[x];
 80         x-=lbt(x);
 81     }
 82     return ans;
 83 }
 84 /*---以上为树状数组基本操作---*/
 85 signed main(){
 86     Read(n),Read(m);//第一轮输入原数组,从小到大排序 
 87     for(rg int i=1;i<=n;i++){
 88         Read(a[i].num),a[i].idx=i;
 89     }
 90     sort(a+1,a+1+n);
 91     
 92     a[0].num=a[n+1].num=INF;//第二轮预处理出所有可用配对 
 93     for(rg int i=1;i<=n;i++){
 94         int lft=a[i].num-a[i-1].num,rit=a[i].num-a[i+1].num;
 95         if(abs(lft)<=abs(rit)){//用左边的 
 96             c[++tot].l=min(a[i].idx,a[i-1].idx);
 97             c[tot].r=max(a[i].idx,a[i-1].idx);
 98         }
 99         if(abs(lft)>=abs(rit)){//用右边的(注意此处) 
100             c[++tot].l=min(a[i].idx,a[i+1].idx);
101             c[tot].r=max(a[i].idx,a[i+1].idx);
102         }
103     }
104     sort(c+1,c+1+tot);
105     
106     for(rg int i=1;i<=m;i++){//第三轮输入询问,排序 
107         Read(q[i].l),Read(q[i].r),q[i].idx=i;
108     }
109     sort(q+1,q+1+m);
110     
111     for(rg int i=m;i>=1;i--){//第四轮求答案 
112         while(q[i].l<=c[tot].l){//往左跑到头 
113             add(c[tot].r,1);
114             tot--;
115         }
116         ans[q[i].idx]=query(q[i].r);//记录一下答案 
117     }
118     int Ans=0;
119     for(rg int i=1;i<=m;i++){
120         Ans+=ans[i]*i;
121     }
122     cout<<Ans<<endl;
123     return 0;
124 }

完结撒花

posted @ 2020-03-23 21:45  ajthreac  阅读(371)  评论(1编辑  收藏  举报