Codeforces_961E_Tufurama(树状数组or线段树)

版权声明: https://blog.csdn.net/yyy_3y/article/details/79965770

传送门

题意:有一部电视剧有n季,每一季有ai集。问有多少对i,j存在第i季第j集也同时存在第j季第i集。
思路:核心问题还是统计对于第i季,你要统计第i行(存在多少数量,要大于i)。
线段树的维护相对而言比较暴力,树状数组的话,一开始全是1,一旦某个数过小,就会导致不构成贡献,移除就好。

tips:对于这样的题目,可以首先思考排序,这样可以省去一维操作。

线段树:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
vector<int> stk[N<<2];
int a[N];
void build(int x,int l,int r)
{
    if(l==r){
        stk[x].push_back(a[l]);
        return;
    }
    for(int i=l;i<=r;i++) stk[x].push_back(a[i]);
    sort(stk[x].begin(),stk[x].end());
    int mid=(l+r)/2;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
int query(int L,int R,int x,int l,int r)
{
    if(L>R) return 0;
    if(L<=l && r<=R)
        return stk[x].size()-(lower_bound(stk[x].begin(), stk[x].end(), L - 1) - stk[x].begin());
    int ans=0;
    int mid=(l+r)/2;
    if(L<=mid) ans+=query(L,R,x<<1,l,mid);
    if(R>mid) ans+=query(L,R,x<<1|1,mid+1,r);
    return ans;
}
int main ()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);
    long long ans=0;
    for(int i=1;i<n;i++){
       ans+=query(i+1,min(a[i],n),1,1,n);
    }
    printf("%lld\n",ans);
    return 0;
}

树状数组:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int a[N],f[N];
struct node{
    int x,y;
}p[N];
bool cmp(node a,node b)
{
    if(a.y==b.y) return a.x<b.x;
    return a.y<b.y;
}
void add(int x,int y)
{
    while(x<N){
        f[x]+=y;
        x+=x&-x;
    }
}
int query(int x)
{
    int ans=0;
    while(x){
        ans+=f[x];
        x-=x&-x;
    }
    return ans;
}
int main ()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]=min(a[i],n),p[i].x=i,p[i].y=a[i],add(i,1);
    sort(p+1,p+1+n,cmp);
    long long ans=0;
    int now=1;
    for(int i=1;i<=n;i++){
        while(now<=n && p[now].y<i) add(p[now++].x,-1);
        ans+=query(a[i]);
        if(a[i]>=i) ans--;
    }
    printf("%lld\n",ans/2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yyy_3y/article/details/79965770