题解【[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\) 和 \(B_y\) 。

每次交换后,你都要给出答案。
\[
\texttt{Solution}
\]

  • CF785E 升级版,多加了 \(B\) 这一维度。
  • 首先,对于初始序列的答案,显然可以用两个 \(\text{BIT}\) 来做,一个 \(\text{BIT}\) 维护值域内数的数量,一个 \(\text{BIT}\) 维护值域内数的 \(B\) 值和 ,像 \(\text{BIT}\) 求逆序对一样地,从后往前扫,考虑当前位置 \(x\) 上的数 \(A_x\) ,与 \(x+1\) 到 \(n\) 之间的 \(A_y\) 产生的逆序对对答案的贡献,记 \(C_1\) 为 \(A\) 值在 \([1,A_x-1]\) 内数的个数,记 \(C_2\) 为 \(A\) 值在 \([1,A_x-1]\) 内数的 \(B\) 值和,则对答案的贡献有 \(B_x \times C_1+C_2\) ,求完整个序列的答案的复杂度是 \(\mathcal{O(n \log n)}\) 的。
  • 考虑交换怎么搞,对于任意两个位置 \(x,y\) ,我们考虑计算出交换 \(x,y\) 对答案造成的影响。
  • 首先,交换 \(x,y\) 显然不会影响到 \([1,x-1]\) 和 \([y+1,n]\) 中的数与 \(A_x\) 或 \(A_y\) 产生的逆序对,因为相对位置是不变的。再者,对于 \(A_x\) 和 \(A_y\) 是否会形成逆序对,可以直接讨论一下 \(A_x\) 和 \(A_y\) 的大小关系。
  • 于是我们只要考虑 \([x+1,y-1]\) 内的数与 \(A_x\) 和 \(A_y\) 的逆序对关系,我们可以视交换 \(x,y\) 为:在 \(x\) 处除去一个 \(A_x\) ,在 \(x\) 处增上一个 \(A_y\) ,在 \(y\) 处除去一个 \(A_y\) ,在 \(y\) 处增上一个 \(A_x\) 。
  • 那么答案的变化应该为:

    减去 " \(B_x\times\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([1,A_x-1]\) 的数的个数 \(+\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([1,A_x-1]\) 的数的 \(B\) 值和 " 。

    加上 " \(B_y\times\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([1,A_y-1]\) 的数的个数 \(+\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([1,A_y-1]\) 的数的 \(B\) 值和 " 。

    减去 " \(B_y\times\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([A_y+1,n]\) 的数的个数 \(+\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([A_y+1,n]\) 的数的 \(B\) 值和 " 。

    加上 " \(B_x\times\) 区间 \([x+1,y-1]\) 内 \(A\) 值在 \([A_x+1,n]\) 的数的个数 \(+\) 区间 \([x+1,y-1]\) 内 \(A\) 值在\([A_x+1,n]\) 的数的 \(B\) 值和 " 。

  • 我们发现更新答案需要查询 " 区间内的给定值域内的数的信息 " ,并且带修,可以用树套树来做(\(\text{BIT}\) 套权值线段树,线段树套权值线段树都可以)。
  • \(\mathcal{O(n \log^2 n)}\) ,评测记录

\[
\texttt{Code}
\]

#include<cstdio>
#include<algorithm>

#define RI register int

using namespace std;

namespace IO
{
    static char buf[1<<20],*fs,*ft;
    inline char gc()
    {
        if(fs==ft)
        {
            ft=(fs=buf)+fread(buf,1,1<<20,stdin);
            if(fs==ft)return EOF;
        }
        return *fs++;
    }
    #define gc() getchar()
    inline int read()
    {
        int x=0,f=1;char s=gc();
        while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
        return x*f;
    }
}using IO::read;

const int N=50100,MLOGNLOGN=20000000;

const int Mod=1e9+7;

void ckadd(long long &x,long long val)
{
    x=((x+val)%Mod+Mod)%Mod;
}

int n,m;

long long a[N],b[N];

long long BIT[N][2]; // 0 个数 : 1 和

void BIT_add(int x,int k,int val)
{
    for(;x<=n;x+=x&-x)ckadd(BIT[x][k],val);
}

long long BIT_ask(int x,int k)
{
    long long ans=0;
    for(;x;x-=x&-x)ckadd(ans,BIT[x][k]);
    return ans;
}

long long ans; 

int tot,root[N];
struct SegmentTree{
    int lc,rc;
    long long cnt;
    long long sum;
}t[MLOGNLOGN];

int New()
{
    tot++;
    t[tot].lc=t[tot].rc=t[tot].cnt=0;
    return tot;
}

void insert(int &p,int l,int r,int delta,int cnt,int sum)
{
    if(!p)p=New();
    ckadd(t[p].cnt,cnt);
    ckadd(t[p].sum,sum);
    if(l==r)return;
    int mid=(l+r)/2;
    if(delta<=mid)
        insert(t[p].lc,l,mid,delta,cnt,sum);
    else
        insert(t[p].rc,mid+1,r,delta,cnt,sum);
}

void add(int pos,int delta,int cnt,int sum)
{
    for(;pos<=n;pos+=pos&-pos)
        insert(root[pos],1,n,delta,cnt,sum);
}

int lenA,addt[N];
int lenS,subt[N];

long long ask(int L,int R,int k,int s,int e) // 区间 [l,r] 类型 k 值域 [s,e]
{
    if(e==0||s>e)
        return 0;

    long long res=0;
    int l,r;

    lenA=0;
    for(RI i=R;i;i-=i&-i)addt[++lenA]=root[i];
    lenS=0;
    for(RI i=L-1;i;i-=i&-i)subt[++lenS]=root[i];

    l=1,r=n;
    while(true)
    {
        if(l==r)
        {
            for(RI i=1;i<=lenA;i++)ckadd(res,k?t[addt[i]].sum:t[addt[i]].cnt);
            for(RI i=1;i<=lenS;i++)ckadd(res,k?-t[subt[i]].sum:-t[subt[i]].cnt);
            break;
        }
        int mid=(l+r)/2;
        if(e<=mid)
        {
            for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].lc;
            for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].lc;
            r=mid;
        }
        else
        {
            for(RI i=1;i<=lenA;i++)ckadd(res,k?t[t[addt[i]].lc].sum:t[t[addt[i]].lc].cnt);
            for(RI i=1;i<=lenS;i++)ckadd(res,k?-t[t[subt[i]].lc].sum:-t[t[subt[i]].lc].cnt);
            for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].rc;
            for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].rc;
            l=mid+1;
        }
    }

    if(s==1)
        return res;

    lenA=0;
    for(RI i=R;i;i-=i&-i)addt[++lenA]=root[i];
    lenS=0;
    for(RI i=L-1;i;i-=i&-i)subt[++lenS]=root[i];

    l=1,r=n;
    while(true)
    {
        if(l==r)
        {
            for(RI i=1;i<=lenA;i++)ckadd(res,k?-t[addt[i]].sum:-t[addt[i]].cnt);
            for(RI i=1;i<=lenS;i++)ckadd(res,k?t[subt[i]].sum:t[subt[i]].cnt);
            break;
        }
        int mid=(l+r)/2;
        if(s-1<=mid)
        {
            for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].lc;
            for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].lc;
            r=mid;
        }
        else
        {
            for(RI i=1;i<=lenA;i++)ckadd(res,k?-t[t[addt[i]].lc].sum:-t[t[addt[i]].lc].cnt);
            for(RI i=1;i<=lenS;i++)ckadd(res,k?t[t[subt[i]].lc].sum:t[t[subt[i]].lc].cnt);
            for(RI i=1;i<=lenA;i++)addt[i]=t[addt[i]].rc;
            for(RI i=1;i<=lenS;i++)subt[i]=t[subt[i]].rc;
            l=mid+1;
        }
    }

    return res;
}

void turn(int x,int y)
{
    if(x>y)
        swap(x,y);

    if(x+1<=y-1)
    {
        ckadd(ans,-b[x]*ask(x+1,y-1,0,1,a[x]-1)-ask(x+1,y-1,1,1,a[x]-1));
        ckadd(ans,-b[y]*ask(x+1,y-1,0,a[y]+1,n)-ask(x+1,y-1,1,a[y]+1,n));
        ckadd(ans,b[y]*ask(x+1,y-1,0,1,a[y]-1)+ask(x+1,y-1,1,1,a[y]-1));
        ckadd(ans,b[x]*ask(x+1,y-1,0,a[x]+1,n)+ask(x+1,y-1,1,a[x]+1,n));
    }

    if(a[x]>a[y])
        ckadd(ans,-b[x]-b[y]);
    else if(a[x]<a[y])
        ckadd(ans,b[x]+b[y]);

    add(x,a[x],-1,-b[x]);
    add(y,a[y],-1,-b[y]);

    swap(a[x],a[y]),swap(b[x],b[y]);

    add(x,a[x],1,b[x]);
    add(y,a[y],1,b[y]);
}

int main()
{
    n=read(),m=read();

    for(RI i=1;i<=n;i++)
        a[i]=read(),b[i]=read(),
        add(i,a[i],1,b[i]);

    for(RI i=n;i;i--)
    {
        ckadd(ans,b[i]*BIT_ask(a[i]-1,0)+BIT_ask(a[i]-1,1));
        BIT_add(a[i],0,1),BIT_add(a[i],1,b[i]);
    }

    while(m--)
    {
        int x=read(),y=read();

        turn(x,y);

        printf("%lld\n",ans);
    }

    return 0;
}

\[
\texttt{Thanks} \ \texttt{for} \ \texttt{watching}
\]

原文地址:https://www.cnblogs.com/cjtcalc/p/12431643.html

时间: 2024-08-17 02:58:48

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

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

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

洛谷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分治,想锻炼一下代码能力所以写了树套树. 首先这是一个求逆

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

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

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

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

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

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

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-

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

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

网易极客战记官方攻略-地牢-Kithgard 图书管理员

关卡连接: https://codecombat.163.com/play/level/kithgard-librarian 向友好的图书馆管理员求助! 简介 大多数关卡都有提示,在你卡关时挺有用. 点击 下一个 来查看关卡的所有提示. 在这关,点击'Next' 找到图书馆大门的密码! 默认代码 # 你需要图书馆大门的开门密码! # 密码就在 提示 中! # 请点击代码窗口上的蓝色 “提示” 按钮. # 如果你在关卡中遇到了困难,请点击 “提示” 按钮! hero.moveRight() her

洛谷 P3955 图书管理员【模拟/思维】

题目描述 图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个 正整数. 每位借书的读者手中有一个需求码,这个需求码也是一个正整数.如果一本书的图 书编码恰好以读者的需求码结尾,那么这本书就是这位读者所需要的. 小 D 刚刚当上图书馆的管理员,她知道图书馆里所有书的图书编码,她请你帮她写 一个程序,对于每一位读者,求出他所需要的书中图书编码最小的那本书,如果没有他 需要的书,请输出-1. 输入输出格式 输入格式: 输入文件的第一行,包含两个正整数 n 和 q,以一个空格分开,