【GDOI2018模拟7.12】A

Description

这里写图片描述
n,m<=1e5,注意题目中的低于是指小于等于

Solution

把逆序对拆成每个点后面小于它的点的个数和
考虑一次操作,影响的点之后这个位置后面权值<=当前权值的点的贡献
这些点的权值会变成0,其他点的权值不会变
那么我们可以离线处理出每个点最早一次被修改的时间,然后对把它对于某一段答案的贡献减掉
这个东西听说可以用树状数组做,然而由于我太菜用了线段树_ (:з」∠) _

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e5+5;
struct note{int v,id;}b[N];
bool cmp(note x,note y) {return x.v<y.v;}
int n,m,tot,a[N],tr[N],id[N],v[N],tree[N*5],doit[N],x;
ll an[N],val[N],ans;
void insert(int x) {
    for(;x<=tot;x+=x&-x) tr[x]++;
}
int find(int x) {
    int res=0;
    for(;x;x-=x&-x) res+=tr[x];
    return res;
}
void change(int x,ll y) {
    for(;x<=m;x+=x&-x) an[x]+=y;
}
ll query(int x) {
    ll ans=0;
    for(;x;x-=x&-x) ans+=an[x];
    return ans;
}
void modify(int v,int l,int r,int x,int y) {
    if (l==r) {
        if (!tree[v]) tree[v]=y;
        else tree[v]=min(tree[v],y);
        return;
    }
    int m=(l+r)/2;
    if (x<=m) modify(v*2,l,m,x,y);
    else modify(v*2+1,m+1,r,x,y);
    tree[v]=min(tree[v*2],tree[v*2+1]);
}
int get(int v,int l,int r,int x,int y) {
    if (l==x&&r==y) return tree[v];
    int m=(l+r)/2;
    if (y<=m) return get(v*2,l,m,x,y);
    else if (x>m) return get(v*2+1,m+1,r,x,y);
    else return min(get(v*2,l,m,x,m),get(v*2+1,m+1,r,m+1,y));
}
int main() {
    scanf("%d%d",&n,&m);
    fo(i,1,n) scanf("%d",&a[i]);
    fo(i,1,n) b[i].v=a[i],b[i].id=i;
    sort(b+1,b+n+1,cmp);
    fo(i,1,n) {
        if (i==1||b[i].v!=b[i-1].v) tot++;
        a[b[i].id]=tot;
    } 
    fd(i,n,1) {
        val[i]=find(a[i]-1);
        ans+=val[i];
        insert(a[i]);
    }
    printf("%lld\n",ans);change(1,ans);
    fo(i,1,n) doit[i]=m+1;
    fo(i,1,tot) modify(1,1,tot,i,m+1);
    fo(i,1,m) {
        scanf("%d",&x);
        if (doit[x]==m+1) doit[x]=i;
    }
    fo(i,1,n) {
        modify(1,1,tot,a[i],doit[i]);
        int fir=get(1,1,tot,a[i],tot);
        if (fir<=m) change(fir,-val[i]);
    }
    fo(i,1,m) printf("%lld\n",query(i));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值