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));
}