洛谷P3759 - [TJOI2017]不勤劳的图书管理员

Portal

Description

给出一个\(1..n(n\leq5\times10^4)\)的排列\(\{a_n\}\)和数列\(\{w_n\}(w_i\leq10^5)\),进行\(m(m\leq5\times10^4)\)次操作:
交换\(a_{p_1},a_{p_2}\),并求\(\sum_{i=1}^n \sum_{j=i+1}^n [a_i>a_j](w_{a_i}+w_{a_j})\)。

Solution

树套树/CDQ分治,想锻炼一下代码能力所以写了树套树。
首先这是一个求逆序对的问题,那么我们考虑交换\(a_{p_1},a_{p_2}\)对答案有什么影响。易知只有\(i\in[p_1+1,p_2-1]\)对答案造成影响:
若\(a_i<a_{p_1}\),那么答案减\(a_i+a_{p_1}\);若\(a_i>a_{p_1}\),那么答案加\(a_i+a_{p_1}\)。
若\(a_i<a_{p_2}\),那么答案加\(a_i+a_{p_2}\);若\(a_i>a_{p_2}\),那么答案减\(a_i+a_{p_2}\)。
那么求出\(\{c_1,s_1,c_2,s_2\}\)分别表示区间\([p_1+1,p_2-1]\)内小于/大于\(x\)的\(i\)的个数/\(w_i\)的和,然后就可以根据上两行计算答案的变化。而这可以用树套树来做。

时间复杂度\(O(mlog^2n)\)。

Code

//[TJOI2017]不勤劳的图书管理员
#include <algorithm>
#include <cstdio>
using std::swap;
typedef long long lint;
inline char gc()
{
    static char now[1<<16],*s,*t;
    if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
    return *s++;
}
inline int read()
{
    int x=0; char ch=gc();
    while(ch<'0'||'9'<ch) ch=gc();
    while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x;
}
const int N=5e4+10;
const int P=1e9+7;
int n,m,a[N],w[N];
namespace init
{
    int tr1[N],tr2[N];
    void add(int x,int v) {while(x<=n) tr1[x]++,tr2[x]=(tr2[x]+v)%P,x+=x&(-x);}
    int sum1(int x) {int r=0; while(x) r+=tr1[x],x-=x&(-x); return r;}
    int sum2(int x) {int r=0; while(x) r=(r+tr2[x])%P,x-=x&(-x); return r;}
    int calc()
    {
        int res=0;
        for(int i=1;i<=n;i++)
        {
            res=(res+(lint)sum1(n-a[i]+1)*w[a[i]])%P;
            res=(res+sum2(n-a[i]+1))%P;
            add(n-a[i]+1,w[a[i]]);
        }
        return res;
    }
}
const int N1=2e6;
struct info
{
    int c1,s1,c2,s2;
    info(int _c1=0,int _s1=0,int _c2=0,int _s2=0) {c1=_c1,s1=_s1,c2=_c2,s2=_s2;};
    friend info operator +(info x,info y)
    {
        int c1=x.c1+y.c1,s1=x.s1+y.s1,c2=x.c2+y.c2,s2=x.s2+y.s2;
        return info(c1,s1%P,c2,s2%P);
    }
};
namespace inTr
{
    int cnt,fa[N1],ch[N1][2],siz[N1]; int val[N1],sum[N1];
    inline void update(int p)
    {
        siz[p]=siz[ch[p][0]]+1+siz[ch[p][1]];
        sum[p]=((sum[ch[p][0]]+sum[ch[p][1]])%P+w[val[p]])%P;
    }
    inline int wh(int p) {return p==ch[fa[p]][1];}
    inline void rotate(int p)
    {
        int q=fa[p],r=fa[q],w=wh(p);
        fa[p]=r; if(r) ch[r][wh(q)]=p;
        fa[ch[q][w]=ch[p][w^1]]=q;
        fa[ch[p][w^1]=q]=p;
        update(q),update(p);
    }
    void splay(int &rt,int p)
    {
        for(int q=fa[p];q;rotate(p),q=fa[p]) if(fa[q]) rotate(wh(p)^wh(q)?p:q);
        update(rt=p);
    }
    int pre(int rt,int x)
    {
        int r=0;
        for(int p=rt;p;p=ch[p][val[p]<x]) if(val[p]<x) r=p;
        return r;
    }
    void ins(int &rt,int p)
    {
        if(!rt) {rt=p; return;}
        int q=rt;
        while(ch[q][val[q]<val[p]]) q=ch[q][val[q]<val[p]];
        fa[ch[q][val[q]<val[p]]=p]=q;
        update(q); splay(rt,p);
    }
    int del(int &rt,int x)
    {
        if(x==0||!rt) return 0;
        int p=pre(rt,x+1); if(val[p]!=x) return 0;
        splay(rt,p);
        int chCnt=(ch[p][0]>0)+(ch[p][1]>0);
        if(chCnt==0) {rt=0; return p;}
        if(chCnt==1) {rt=ch[p][ch[p][1]>0],fa[rt]=0; return p;}
        int q=ch[p][0]; while(ch[q][1]) q=ch[q][1];
        splay(rt,q);
        fa[ch[q][1]=ch[p][1]]=q;
        fa[p]=ch[p][0]=ch[p][1]=0;
        update(q);
        return p;
    }
    inline void change(int &rt,int x1,int x2)
    {
        int p=del(rt,x1); if(!p) p=++cnt;
        fa[p]=ch[p][0]=ch[p][1]=0,siz[p]=1;
        val[p]=x2,sum[p]=w[x2];
        ins(rt,p);
    }
    info query(int rt,int x)
    {
        int c1=0,s1=0;
        for(int p=rt;p;p=ch[p][val[p]<x])
            if(val[p]<x) c1+=siz[p]-siz[ch[p][1]],s1=(s1+sum[p]-sum[ch[p][1]]+P)%P;
        return info(c1,s1,siz[rt]-c1,sum[rt]-s1);
    }
}
namespace outTr
{
    #define Ls (p<<1)
    #define Rs (p<<1|1)
    int rt[N<<2];
    int in(int L,int R,int p1,int p2) {return ((L<=p2&&p2<=R)<<1)|(L<=p1&&p1<=R);}
    void change(int p,int L0,int R0,int p1,int p2)
    {
        int inP=in(L0,R0,p1,p2);
        if(inP==1) inTr::change(rt[p],a[p1],a[p2]);
        else if(inP==2) inTr::change(rt[p],a[p2],a[p1]);
        if(L0==R0) return;
        int mid=L0+R0>>1;
        if(in(L0,mid,p1,p2)) change(Ls,L0,mid,p1,p2);
        if(in(mid+1,R0,p1,p2)) change(Rs,mid+1,R0,p1,p2);
    }
    int optL,optR;
    info query(int p,int L0,int R0,int x)
    {
        if(optL<=L0&&R0<=optR) return inTr::query(rt[p],x);
        int mid=L0+R0>>1; info res=info(0,0,0,0);
        if(optL<=mid) res=res+query(Ls,L0,mid,x);
        if(mid<optR) res=res+query(Rs,mid+1,R0,x);
        return res;
    }
}
inline void change(int p1,int p2) {outTr::change(1,1,n,p1,p2);}
inline info query(int L,int R,int x)
{
    outTr::optL=L,outTr::optR=R;
    return outTr::query(1,1,n,x);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        a[0]=read(),w[a[0]]=read();
        change(0,i); swap(a[0],a[i]);
    }
    int ans=init::calc();
    for(int i=1;i<=m;i++)
    {
        int p1=read(),p2=read();
        if(p1>p2) swap(p1,p2); else if(p1==p2) {printf("%d\n",ans); continue;}
        int x=a[p1],y=a[p2];
        info r1=query(p1+1,p2-1,x),r2=query(p1+1,p2-1,y);
        ans-=((lint)r1.c1*w[x]%P+r1.s1)%P; ans=(ans+P)%P;
        ans+=((lint)r1.c2*w[x]%P+r1.s2)%P; ans=ans%P;
        ans+=((lint)r2.c1*w[y]%P+r2.s1)%P; ans=ans%P;
        ans-=((lint)r2.c2*w[y]%P+r2.s2)%P; ans=(ans+P)%P;
        if(x<y) ans=(ans+w[x]+w[y])%P; else ans=(ans-w[x]-w[y]+P)%P;
        printf("%d\n",ans);
        change(p1,p2); swap(a[p1],a[p2]);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/VisJiao/p/LgP3759.html

时间: 2024-10-10 13:45:47

洛谷P3759 - [TJOI2017]不勤劳的图书管理员的相关文章

[P3759][TJOI2017]不勤劳的图书管理员(分块+树状数组)

题目描述 加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员.他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生 这两本书页数的和的厌烦度.现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置.因为小豆被要求在接下来的m天中至少 要整理一次图书.小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理. 输入输出格式 输入格式: 第一行会有两个数,n,m分别表示有n本书,m天 接下来n行,每行两

P3759 [TJOI2017]不勤劳的图书管理员 [树套树]

树套树是什么啊我不知道/dk 我只知道卡常数w // by Isaunoya #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC optimize( "inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2,-ffast-math,-fsched-spec,unroll-

【bzoj4889】: [Tjoi2017]不勤劳的图书管理员 分块-BIT

[bzoj4889]: [Tjoi2017]不勤劳的图书管理员 题目大意:给定一个序列(n<=50000),每个数有一个编码ai(ai<=50000)和权值vi(vi<=100000),每次交换两个数的位置,交换m次(m<=50000) 求每次交换后的序列的杂乱度对1e9+7取模(对于每一对是逆序对的编码会产生两者权值和的杂乱度). 感觉正解是什么奇怪的树套树?蒟蒻只会分块水题.. 先用BIT求一遍初始状态的杂乱度..(不要问我为什么一开始是BIT..因为打暴力正好用到就懒得改了.

4889: [Tjoi2017]不勤劳的图书管理员 树套树

国际惯例的题面(Bzoj没有,洛谷找的):动态加权逆序对,一眼树套树.256MB内存,5e4范围,不虚不虚.首先把交换改成两个插入和两个删除.考虑插入和删除的贡献,就是统计前面比这个值大的数的数值和,数量和,后面比这个值小的数的数值和,数量和.然后特判一下当前两个值构成逆序对的情况即可(因为这种情况会被计算两遍).考虑树状数组套动态开点线段树维护这个东西,线段树只需要单点修改区间求和即可,十分简单.然而数组开不下啊......理论上我们数组范围要开到2e7左右,然而并跑不满,开到1.4e7就足以

[bzoj4889] [Tjoi2017]不勤劳的图书管理员

Description 加里敦大学有个帝国图书馆,小豆是图书馆阅览室的一个书籍管理员.他的任务是把书排成有序的,所以无序的书让他产生厌烦,两本乱序的书会让小豆产生这两本书页数的和的厌烦度.现在有n本被打乱顺序的书,在接下来m天中每天都会因为读者的阅览导致书籍顺序改变位置.因为小豆被要求在接下来的m天中至少要整理一次图书.小豆想知道,如果他前i天不去整理,第i天他的厌烦度是多少,这样他好选择厌烦度最小的那天去整理. Input 第一行会有两个数,n,m分别表示有n本书,m天 接下来n行,每行两个数

题解【[TJOI2017]不勤劳的图书管理员】

\[ \texttt{Description} \] 给两个长度为 \(n\) 的数列 \(A\) 和 \(B\) . 记 \(A\) 的逆序对(满足 \(x<y\),\(A_x>A_y\) 的数对 \((x,y)\) )对答案的贡献为 \(B_x+B_y\) . 通俗地说,答案就是 \(\sum\limits_{x < y \ \& \ A_x > A_y}\) \(B_x+B_y\). 一共 \(m\) 次交换,每次交换 \(A_x\) 和 \(A_y\) ,\(B_x

洛谷P3761:[TJOI2017]城市

题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用.小明对这个地区深入研究后,觉得这个地区的交通费用太贵.小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用

洛谷P3758:[TJOI2017]可乐

题目描述 加里敦星球的人们特别喜欢喝可乐.因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上.这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆.它每一秒都会随机触发一种行为.现 在给加里敦星球城市图,在第0秒时可乐机器人在1号城市,问经过了t秒,可乐机器人的行为方案数是多少? 输入输出格式 输入格式 第一行输入两个正整数况N,M,N表示城市个数,M表示道路个数.(1 <= N <=30,0 < M < 100) 接下来M行输入u,v,表示u,

洛谷 P3955 图书管理员【民间数据】

P3955 图书管理员[民间数据] 题目背景 数据已再次修正 (既然你们不要前导0我就去掉了) 题目描述 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个 正整数. 每位借书的读者手中有一个需求码,这个需求码也是一个正整数.如果一本书的图 书编码恰好以读者的需求码结尾,那么这本书就是这位读者所需要的. 小 D 刚刚当上图书馆的管理员,她知道图书馆里所有书的图书编码,她请你帮她写 一个程序,对于每一位读者,求出他所需要的书中图书编码最小的那本书,如果没有他 需要的书,请输出