2020牛客寒假算法基础集训营4 H.坐火车(树状数组/线段树)
感觉网上的题解都略微简略包括官方题解,所以打算写一个尽可能详细的
题目链接:https://ac.nowcoder.com/acm/contest/3005/H
题意:
每个车厢都有三个参数,colori ,li ,ri ,询问对于每个车厢,在其两边的车厢有多少对车厢颜色相同,并且颜色的范围在[li , ri]内
思路:
首先我们定义几个数组,pre[i]代表整个数组的前缀中color[i]的个数,suf[i]代表整个数组的后缀中color[i]的个数,这读入的过程中我们就先统计suf数组,树状数组sum[i]记录每个颜色的符合相求的对数是多少
之后开始遍历color[i],对于每个color我们计算之前我们就在suf数组中减去它,这样suf数组的意义就是在x之后各个颜色的个数
之后我们在树状数组中减去pre[color[i]]也就是在x之前与x颜色相同的车厢,因为统计的对数是要求在x的两边,所以我们得减去与x匹配的个数
然后就可以在树状数组中查询区间[li , ri]对数了
在统计完之后就有非常重要的一步了,就是在树状数组中加入suf[color[i]]也就是i之后还有多少与i颜色相同的车厢,也就是i位置的贡献了
#include<iostream> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int maxn=5e5+10; ll pre[maxn],suf[maxn],sum[maxn]; int n,l[maxn],r[maxn],color[maxn]; int lowbit(int x){return x&(-x);} void add(int x,int val) { while(x<=n){ sum[x]+=val; x+=lowbit(x); } } ll query(int x) { ll ans=0; while(x>=1){ ans+=sum[x]; x-=lowbit(x); } return ans; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&color[i],&l[i],&r[i]); suf[color[i]]++; } for(int i=1;i<=n;i++){ suf[color[i]]--; add(color[i],-pre[color[i]]); printf("%lld ",query(r[i])-query(l[i]-1)); pre[color[i]]++; add(color[i],suf[color[i]]); } return 0; }