2020牛客寒假训练营4 H 坐火车 树状数组维护乘积

好叭,这个题目的思路实在是太妙了,我不得不要写博客了。

题目大意:

上传图片不成功,去原OJ看叭:H坐火车

题目思路:

题目要求,每个点的左右颜色相同对数有多少对,而且这颜色对数在询问的区间范围内。

首先我们考虑对于每一个点,在区间[li,ri]内的所有的颜色对数应该为:pre[k]*cur[k] (li<=k<=ri) 

也就是左边颜色k的数量*右边颜色k的数量=颜色k的对数,之后在算出 k在[li,ri]之间的总和

由于是区间询问,我们无法for循环遍历,铁定超时,所以说我们考虑是否能维护f[r],f[r]代表左右两边颜色的对数即L*R,之后我们再用树状数组直接询问[li,ri]内便是logn求解的过程。

1.由于这题目是每一个点所以说,我们可以从左到右遍历。起初的话每个颜色的L*R都为0,用一个数组记录一下,后面每个数字的数量。

2.到第i个数字时,后缀的a[i]的数量-1,那么此时L*R->L*(R-1) ->L*R-L ,所以此时我们需要用数组维护a[i]这个点-L(前缀a[i]的数量),然后询问得到答案。

3.在这之后for循环进行完,由于这个中间点会向右移,所以移动完以后,前缀a[i]个数会+1,此时L*R->(L+1)*R->L*R+R ,所以我们后移之后,维护一下a[i]这个点+R,一直这么维护下去就得到了我们所需要的解。

4.思路妙不可言~,以后处理区间两端的合法数量可以参考思路。

AC代码:

/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e13;
const ll maxn=1e6+5;
const int mod=998244353;
const double eps=1e-12;
ll n,m,p;
ll sum[maxn];
struct node{
    int x,y,z;
}save[maxn];
ll a[maxn],b[maxn];
void update(int x,int k){
    while(x<=n){
        sum[x]+=k;
        x+=x&-x;
    }
}
ll query(int x){
    ll ans=0;
    while(x>0){
        ans+=sum[x];
        x-=x&-x;
    }
    return ans;
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&save[i].x,&save[i].y,&save[i].z);
        a[save[i].x]++;
    }
    for(int i=1;i<=n;i++){
        a[save[i].x]--;
        update(save[i].x,-b[save[i].x]);
       // printf("%lld %lld\n",a[save[i].x],b[save[i].x]);
        printf("%lld ",query(save[i].z)-query(save[i].y-1));
        b[save[i].x]++;
        update(save[i].x,a[save[i].x]);
    }
    return 0;
}
/***
5
1 2 3
2 2 3
3 1 2
2 1 2
1 1 2
***/
发布了157 篇原创文章 · 获赞 146 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/104279734