2020算法设计竞赛 H 坐火车

链接:https://ac.nowcoder.com/acm/contest/3005/H
来源:牛客网

大致题意:让我们针对每一个数,求这个数左区间和右区间颜色相同(也就是数字相同)得对数;
比如:左边3个“3'得颜色,右边2个‘3’得颜色,就有2*3=6对;
数据范围为5e5;所以可接受得复杂度为nlogn;
那么我们可以考虑线段树;
如何维护呢?
我们用两个vis去标记左边和右边各种颜色的个数;代码中visa标记的是右区间,visb标记的是左区间;
然后从左往右遍历一遍:
  在某一次操作中,我们对第K个数求值;那么我们需要先把右区间颜色k的减1;
这个时候假如左区间有这个K数,我们就需要将这个数包含的对数求出来,然后update,
这里有个细节,假如k-1==k,那么需要减掉的对数要减1;
因为k-1并没有在上次的操作中计算;
然后,我们就求K-1这个位置的颜色在右区间的个数,然后将这个数update;
在跑一遍线段树求出第K个数的query(l,r,1)即可;
 
 
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=5e5+10;
 5 int visa[maxn];
 6 int visb[maxn];
 7 ll ans[maxn];
 8 struct node
 9 {
10     int val,l,r;
11 }a[maxn];
12 struct haa
13 {
14     int l;int r;
15     ll sum;
16 }tree[maxn<<2];
17 void build(int l,int r,int root)
18 {
19     tree[root].l=l;tree[root].r=r;
20     tree[root].sum=0;
21     if(l==r) return;
22     int mid=l+r>>1;
23     build(l,mid,root<<1);
24     build(mid+1,r,root<<1|1);
25 }
26 void up(int root)
27 {
28     tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
29 }
30 void update(int pos,int val,int root)
31 {
32     int L=tree[root].l;
33     int R=tree[root].r;
34     if(L==R){
35         tree[root].sum+=val;
36         return;
37     }
38     int mid=L+R>>1;
39     if(pos<=mid) update(pos,val,root<<1);
40     else update(pos,val,root<<1|1);
41     up(root);
42 }
43 ll query(int l,int r,int root)
44 {
45     int L=tree[root].l;
46     int R=tree[root].r;
47     if(l<=L&&r>=R){
48         return tree[root].sum;
49     }
50     ll ans=0;
51     int mid=L+R>>1;
52     if(l<=mid) ans+=query(l,r,root<<1);
53     if(r>mid)  ans+=query(l,r,root<<1|1);
54     return ans;
55 }
56 int main()
57 {
58     int n;
59     scanf("%d",&n);
60     for(int i=1;i<=n;i++){
61         scanf("%d%d%d",&a[i].val,&a[i].l,&a[i].r);
62         visa[a[i].val]++;
63     }
64     build(1,maxn,1);
65     ans[1]=0;
66     visa[a[1].val]--;  //右区间--
67     visb[a[1].val]++;  //左区间++;
68     for(int i=2;i<=n;i++){
69         visa[a[i].val]--; //右区间--;
70         if(visb[a[i].val]){ //如果左区间包含这个数的话;
71             //我们就需要减掉与这个数相关的对数;
72             //但是左区间包含i-1这一项;而这一项在上次操作中没有出现;
73             //所以当两者相等的时候,tmp--;
74             int tmp=visb[a[i].val];
75             if(a[i-1].val==a[i].val) tmp--;
76             update(a[i].val,-tmp,1);
77         }
78         int tmp=visa[a[i-1].val];
79         update(a[i-1].val,tmp,1);
80         ans[i]=query(a[i].l,a[i].r,1);
81         visb[a[i].val]++;
82     }
83     for(int i=1;i<=n;i++)
84         printf("%lld ",ans[i]);
85     printf("\n");
86     return 0;
87 }

猜你喜欢

转载自www.cnblogs.com/pangbi/p/12296223.html