BJ 集训测试8 Problem C 插线板

http://www.elijahqi.win/archives/2762
题意:1~n号插线板 每个插线板有一个存在的时间每个人有一个使用时间 问插在哪个插线板可以挪动次数最少 每次一个插线板的插入和取走都只会对他后面的一个产生影响

将每个人的操作拆成两个 然后预处理succ表示我操作这个之后会对后面的哪个插线板产生挪动次数的贡献

然后分块 表示这段时间之前到所有点的区间挪动最优值处理出来 针对每个操作使用可持久化块状数组维护 (查询o(1))查询的时候因为我块前面的已经处理出来了 那么显然我只需要考虑我这个块到我这个人需要时间的这些区域里的最小值 我已在sqrt的时间处理出来

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define bk 350
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct Persistent_block_array{
    int *p[N/bk+2];
    inline void add(int x){
        const int bl=x/bk;
        int *t=new int[bk];
        if (p[bl]) memcpy(t,p[bl],sizeof(int)*bk);else memset(t,0,sizeof(int)*bk);
        ++t[x%bk];p[bl]=t;
    }
    inline int query(int x){
        const int bl=x/bk;return p[bl]?p[bl][x%bk]:0;
    }
}arr[N];
struct node{
    int id,op,time;
}opt[N];
inline bool cmp(const node &a,const node &b){return a.time<b.time;}
set<int> s;
int succ[N],pre_ans[400][N],sum[N],n,m,q,type,L[N],R[N],last_ans;
int main(){
    freopen("t3.in","r",stdin);
//  freopen("t3.out","w",stdout);
    n=read();m=read();q=read();type=read();int cnt=0;
    for (int i=1;i<=n;++i){
        L[i]=read();R[i]=read();opt[++cnt]=(node){i,1,L[i]};opt[++cnt]=(node){i,0,R[i]};
    }sort(opt+1,opt+(n=cnt)+1,cmp);
    for (int i=1;i<=n;++i){
        if (!opt[i].op) s.erase(opt[i].id);set<int>::iterator it;
        it=s.lower_bound(opt[i].id);if (it!=s.end()) succ[i]=*it;
        if (opt[i].op) s.insert(opt[i].id);
    }
    for (int i=1;i<=n;++i){arr[i]=arr[i-1];
        if (!opt[i-1].op&&succ[i-1]) arr[i].add(succ[i-1]);
        if (opt[i].op&&succ[i]) arr[i].add(succ[i]);
    }
    for (int j=1;j<=n;j+=bk){
        memset(sum,0,sizeof(sum));
        for (int i=j;i<=n;++i) if (succ[i]) ++sum[succ[i]];int mn=inf;
        for (int i=n;i>=j;--i){int id=opt[i].id,su=succ[i];
            if(!opt[i].op&&L[id]<=j) mn=min(mn,sum[id]);
            if (!opt[i].op&&su){
                --sum[su];if (L[su]<=j) mn=min(mn,sum[su]);
            }pre_ans[j/bk][i]=mn;
            if (opt[i].op&&su){
                --sum[su];if (L[su]<=j) mn=min(mn,sum[su]);
            }
        }
    }
    for (int i=1;i<=q;++i){
        int l=read()^last_ans,r=read()^last_ans;int bl=(l-1)/bk,mn=pre_ans[bl][r];
        for (int j=bl*bk+1;j<=l;++j){
            const int id=opt[j].id,su=succ[j];
            if (R[id]>=r) mn=min(mn,arr[r].query(id)-arr[l].query(id));
            if (R[su]>=r) mn=min(mn,arr[r].query(su)-arr[l].query(su));
        }last_ans=mn;
        printf("%d\n",last_ans!=inf?last_ans:-1);
        last_ans=type==0?0:last_ans;last_ans=last_ans==inf?0:last_ans;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值