【JZOJ5222】【GDOI2018模拟7.12】A

55 篇文章 0 订阅
14 篇文章 0 订阅

Description

这里写图片描述
注意:题意有误,IQ小于等于的都会听从指挥

Data Constraint

这里写图片描述

Solution

这道题,有两个坑,首先,IQ小于等于的都会听从指挥,这在做题时居然没讲,居然还有人AC了!!!其次,士兵的IQ可以为0,这小学都没上就去打仗了吧?
然后我用暴力过了^^但后来还是打了遍正解。
下面讲一下正解。
我们考虑当一个操作在x时,我们发现IQ大于a[x]的其往后的逆序对是不变的,而IQ小于等于a[x]的其往后的逆序对会变为0。所以这就意味着x只会对IQ小于a[x]的后方有影响。换句话说,一次操作会造成影响的:1、标号必须大于x 2、IQ必须小于等于a[x]这是个二维偏序。而且被操作过的点逆序对数将永远变为0。
问题转化成该如何求每个士兵变为0的最早时间。
所以我们先对操作按x从小到大进行排序,维护一个权值线段树维护每个权值最早的修改时间,然后枚举操作,每次操作由x右移至x’时,序列上x到x’-1的士兵往后都不会再被执行操作,所以我们在权值线段树上对应的a取出它的最早修改时间并计入数组。那样往后对这些权值的修改时间进行修改时,也与这些士兵的修改时间无关。于是我们就得出了每个士兵变为0的最早时间。最后我们按修改时间对士兵进行排序,按询问时间统计逆序对数量。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+5;
struct code{
    ll a,b;
}c[maxn],b[maxn];
ll a[maxn],f[maxn*4],g[maxn];
ll n,m,i,t,j,k,l,x,y,z,ans,num,p;
bool cmp(code x,code y){
    return x.a<y.a;
}
void insert(ll x){
    while (x<=n) f[x]++,x+=(x&(-x));
}
ll find(int x){
    ll t=0;
    while (x>0) t+=f[x],x-=(x&(-x));
    return t;
}
void change(int l,int r,int v,int x,int y){
    int mid=(l+r)/2;
    if (f[v]!=p && l!=r){
        f[v*2]=min(f[v*2],f[v]);f[v*2+1]=min(f[v*2+1],f[v]);f[v]=p;
    }
    if (l>=x && r<=y){
        f[v]=min(z,f[v]);
        return;
    }
    if (l<=y && mid>=x) change(l,mid,v*2,x,y);
    if (mid<y && r>=x) change(mid+1,r,v*2+1,x,y);
}
void find1(int l,int r,int v,int x){
    int mid=(l+r)/2;
    if (f[v]!=p && l!=r){
        f[v*2]=min(f[v*2],f[v]);f[v*2+1]=min(f[v*2+1],f[v]);f[v]=p;
    }
    if (l==r){
        t=f[v];return;
    }
    if (x<=mid)find1(l,mid,v*2,x);
    else find1(mid+1,r,v*2+1,x);
}
int main(){
//  freopen("data.in","r",stdin);freopen("data.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (i=1;i<=n;i++)
        scanf("%lld",&a[i]),c[i].a=a[i],c[i].b=i;
    sort(c+1,c+n+1,cmp);num=0;c[0].a=-1;
    for (i=1;i<=n;i++){
        if (c[i].a>c[i-1].a) ++num;
        a[c[i].b]=num;
    }
    for (i=n;i>=1;i--)
        g[i]=find(a[i]-1),ans+=g[i],insert(a[i]);
    printf("%lld\n",ans);
    for (i=1;i<=m;i++)
        scanf("%d",&c[i].a),c[i].b=i;
    sort(c+1,c+m+1,cmp);
    memset(f,127,sizeof(f));p=f[1];j=1; 
    for (i=1;i<=m;i++){ 
        while (j<c[i].a) find1(1,n,1,a[j]),b[j].a=t,b[j].b=g[j],j++;
        z=c[i].b,change(1,n,1,1,a[c[i].a]);
    } 
    while (j<=n) find1(1,n,1,a[j]),b[j].a=t,b[j].b=g[j],j++;
    sort(b+1,b+n+1,cmp);j=1;
    for (i=1;i<=m;i++){
        while (b[j].a<=i && j<=n) ans-=b[j++].b;
        printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值