题面

传送门:[CQOI2011]动态逆序对


Solution

一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗?

然后冷静思考了一分钟,猛然发现单纯的线段树并不能解决这个问题,好像还要在外面再套上一颗树。

这就很shit了。你问我资磁不资磁树套树,我是不资磁的,树套树是暴力数据结构,我能资磁吗?

很不幸,昨天现实狠狠地打了我一脸:时间不够开新坑的,不切题又浑身难受,找了半天题,还是把这道题拉了出来(哈,真香)

不扯淡了,这题还是很显然的。

考虑开倒车,我们一个一个往里面加树,然后统计一下这个数能对当前的数列有多少贡献,贡献很容易想到:我们只需要找到在他后面比他小的数以及在他前面比他大的数就好。

然后本蒟蒻写了个蜜汁线段树套splay。

时间复杂度是$O(n*log^2n)$,空间复杂度为$O(n*logn)$。理论上应该能过

可惜现实非常苦感:

.....

那咋搞啊。

那我上个线段树套权值线段树吧

然后又码了半个小时。

时空复杂度均为$O(n*log^2n)$ (这明显要MLE啊,问题是题解蜜汁能过)

可惜现实依旧苦感:

难道,改数据了?

接着,我copy了一发题解,交上去,A掉了.......

到目前为止,我还是想不通为啥开同样的数组,他A了,我T了。难道说他外层套的树状数组可以有效减少空间的消耗?

想不通,还请个位dalao赐教。


Code (并不能A)

线段树套splay:

#include<iostream>
#include<cstdio>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=100000+1000;
struct TreeInTree
{
#define mid ((now_l+now_r)>>1)
#define lson (now<<1)
#define rson (now<<1|1)
#define root son[r][1]
static const int M=N*25;
int fa[M],son[M][2],size[M],cnt[M],num[M],to;
inline void update(int x)
{
size[x]=size[son[x][0]]+size[son[x][1]]+cnt[x];
}
inline void rotate(int x,int type)
{
int y=fa[x],z=fa[y];
son[z][y==son[z][1]]=x,fa[x]=z;
son[y][!type]=son[x][type],fa[son[x][type]]=y;
son[x][type]=y,fa[y]=x;
update(y),update(x);
}
void splay(int x,int to)
{
while(fa[x]!=to)
{
if(x==son[fa[x]][fa[x]==son[fa[fa[x]]][1]] and fa[fa[x]]!=to)
rotate(fa[x],x==son[fa[x]][0]);
rotate(x,x==son[fa[x]][0]);
}
}
void Insert(int w,int r)
{
if(root==0)
{
root=++to,fa[root]=r;
num[root]=w,update(root);
return;
}
int now=root,last=root;
while(now!=0)
last=now,now=son[now][w>num[now]];
now=++to,fa[now]=last,son[last][w>num[last]]=now;
num[now]=w,update(now);
splay(now,r);
}
int Query1(int x,int r)
{
int now=root,ans=0;
while(now!=0)
{
if(num[now]>=x)
now=son[now][0];
else
{
if(num[now]>num[ans]) ans=now;
now=son[now][1];
}
}
if(ans==0) return 0;
splay(ans,r);
return size[son[ans][0]]+cnt[ans];
}
int Query2(int x,int r)
{
int now=root,ans=0;
num[0]=0x3f3f3f3f;
while(now!=0)
{
if(num[now]>x)
{
if(num[now]<num[ans]) ans=now;
now=son[now][0];
}
else
now=son[now][1];
}
num[0]=0;
if(ans==0) return 0;
splay(ans,r);
return size[son[ans][1]]+cnt[ans];
}
int t[N<<2];
void Build(int now,int now_l,int now_r)
{
t[now]=++to;
if(now_l==now_r) return;
Build(lson,now_l,mid);
Build(rson,mid+1,now_r);
}
inline void Insert2(int x,int w,int now,int now_l,int now_r)
{
Insert(w,t[now]);
if(now_l!=now_r)
{
if(x<=mid) Insert2(x,w,lson,now_l,mid);
else Insert2(x,w,rson,mid+1,now_r);
}
}
int Query3(int l,int r,int w,int type,int now,int now_l,int now_r)
{
if(now_l>=l and now_r<=r)
{
if(type==1) return Query1(w,t[now]);
else return Query2(w,t[now]);
}
int sum=0;
if(l<=mid) sum+=Query3(l,r,w,type,lson,now_l,mid);
if(r>mid) sum+=Query3(l,r,w,type,rson,mid+1,now_r);
return sum;
}
#undef mid
#undef lson
#undef rson
}tit;
int n,m,p[N],q[N],unOK[N];
long long ans[N];
int main()
{
freopen("3157.in","r",stdin);
freopen("3157.out","w",stdout); int t=clock();
n=read(),m=read();
for(int i=1;i<=n;i++)
p[read()]=i;
for(int i=1;i<=m;i++)
q[i]=read(),unOK[q[i]]=true; tit.Build(1,1,n);
for(int i=1;i<=n;i++)
if(unOK[i]==false)
{
tit.Insert2(p[i],i,1,1,n);
ans[m+1]+=tit.Query3(p[i],n,i,1,1,1,n)+tit.Query3(1,p[i],i,2,1,1,n);
}
for(int i=m;i>=1;i--)
{
tit.Insert2(p[q[i]],q[i],1,1,n);
ans[i]=ans[i+1]+tit.Query3(p[q[i]],n,q[i],1,1,1,n)+tit.Query3(1,p[q[i]],q[i],2,1,1,n);
} for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
cerr<<clock()-t<<endl;
return 0;
}

线段树套权值线段树

#include<iostream>
#include<cstdio>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=100000+1000;
int n,m,p[N],q[N],unOK[N];
long long ans[N];
struct TreeInTree
{
#define mid ((now_l+now_r)>>1)
#define lson (now<<1)
#define rson (now<<1|1)
static const int M=N*200;
int son[M][2],num[M],to;
inline void update(int x)
{
num[x]=num[son[x][0]]+num[son[x][1]];
}
void Insert(int x,int now,int now_l,int now_r)
{
if(now_l==now_r)
{
num[now]++;
return;
}
if(now>to)
cerr<<to;
if(x<=mid)
{
if(son[now][0]==0) son[now][0]=++to;
Insert(x,son[now][0],now_l,mid);
}
else
{
if(son[now][1]==0) son[now][1]=++to;
Insert(x,son[now][1],mid+1,now_r);
}
update(now);
}
int Query1(int l,int r,int now,int now_l,int now_r)
{
if(l>r) return 0;
if((now_l>=l and now_r<=r) or now==0)
return num[now];
int t_ans=0;
if(l<=mid) t_ans+=Query1(l,r,son[now][0],now_l,mid);
if(r>mid) t_ans+=Query1(l,r,son[now][1],mid+1,now_r);
return t_ans;
}
int t[N<<2];
void Build(int now,int now_l,int now_r)
{
t[now]=++to;
if(now_l==now_r) return;
Build(lson,now_l,mid);
Build(rson,mid+1,now_r);
}
inline void Insert2(int x,int w,int now,int now_l,int now_r)
{
Insert(w,t[now],1,n);
if(now_l!=now_r)
{
if(x<=mid) Insert2(x,w,lson,now_l,mid);
else Insert2(x,w,rson,mid+1,now_r);
}
}
int Query3(int l,int r,int w,int type,int now,int now_l,int now_r)
{
if(now_l>=l and now_r<=r)
{
if(type==1) return Query1(1,w-1,t[now],1,n);
else return Query1(w+1,n,t[now],1,n);
}
int sum=0;
if(l<=mid) sum+=Query3(l,r,w,type,lson,now_l,mid);
if(r>mid) sum+=Query3(l,r,w,type,rson,mid+1,now_r);
return sum;
}
#undef mid
#undef lson
#undef rson
}tit;
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
p[read()]=i;
for(int i=1;i<=m;i++)
q[i]=read(),unOK[q[i]]=true; tit.Build(1,1,n);
for(int i=1;i<=n;i++)
if(unOK[i]==false)
{
tit.Insert2(p[i],i,1,1,n);
ans[m+1]+=tit.Query3(p[i],n,i,1,1,1,n)+tit.Query3(1,p[i],i,2,1,1,n);
}
for(int i=m;i>=1;i--)
{
tit.Insert2(p[q[i]],q[i],1,1,n);
ans[i]=ans[i+1]+tit.Query3(p[q[i]],n,q[i],1,1,1,n)+tit.Query3(1,p[q[i]],q[i],2,1,1,n);
} for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}

[Luogu P3157][CQOI2011]动态逆序对 (树套树)的更多相关文章

  1. luogu P3157 [CQOI2011]动态逆序对(CDQ分治)

    题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序 ...

  2. Luogu P3157 [CQOI2011]动态逆序对

    题目链接 \(Click\) \(Here\) 这个题有点卡常数..我的常数比较大所以是吸着氧气跑过去的... 题意:计算对于序列中每个位置\(p\),\([1,p-1]\)区间内比它大的数的个数,和 ...

  3. LUOGU P3157 [CQOI2011]动态逆序对(CDQ 分治)

    传送门 解题思路 cdq分治,将位置看做一维,修改时间看做一维,权值看做一维,然后就转化成了三维偏序,用排序+cdq+树状数组.注意算删除贡献时要做两次cdq,分别算对前面和后面的贡献. #inclu ...

  4. P3157 [CQOI2011]动态逆序对(树状数组套线段树)

    P3157 [CQOI2011]动态逆序对 树状数组套线段树 静态逆序对咋做?树状数组(别管归并QWQ) 然鹅动态的咋做? 我们考虑每次删除一个元素. 减去的就是与这个元素有关的逆序对数,介个可以预处 ...

  5. [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)

    [BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...

  6. P3157 [CQOI2011]动态逆序对

    P3157 [CQOI2011]动态逆序对 https://www.luogu.org/problemnew/show/P3157 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai&g ...

  7. 洛谷 P3157 [CQOI2011]动态逆序对 解题报告

    P3157 [CQOI2011]动态逆序对 题目描述 对于序列\(A\),它的逆序对数定义为满足\(i<j\),且\(A_i>A_j\)的数对\((i,j)\)的个数.给\(1\)到\(n ...

  8. P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)

    P3157 [CQOI2011]动态逆序对 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任 ...

  9. BZOJ_3295_[Cqoi2011]动态逆序对_CDQ分治+树状数组

    BZOJ_3295_[Cqoi2011]动态逆序对_CDQ分治+树状数组 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一 ...

随机推荐

  1. SQL Server 子查询遇到的坑

    这两天改 Bug 时使用 Sql Server 的子查询遇到了一些问题,特此记录一下,之前用 MySQL 比较多,按照 MySQL 的语法其实是没有问题的. 以下面这张表为例: 执行以下 SQL: s ...

  2. 020 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 14 变量与常量 知识总结

    020 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 14 变量与常量 知识总结 本文知识点:变量与常量 知识总结 Java中的标识符 Java中的关键字 目前常 ...

  3. Excel-VLOOKUP函数跨表匹配查找①

    问题场景 对表中的员工进行测评总结,从所有员工考核明细表中匹配这些参与测评的员工的得分和相关信息: 场景一 从所有员工明细表中匹配需要参与测评的员工相关信息. 建了两个sheet页,考核员工表和全员考 ...

  4. Java之线程池解析

    线程池 目录 线程池 线程池概述 创建一个线程池并提交线程任务 线程池源码解析 参数认识 构造方法 提交任务 addWorker 执行任务 关闭线程池 线程池概述 什么是线程池 为什么使用线程池 线程 ...

  5. CentOS之—双网卡双IP双网关配置

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/77487639 一.配置讲解 1.配置DNS 修改对应网卡的DNS的配置文件 # v ...

  6. 深入了解如何构建您的第一个多语言ASP。NET MVC 5 Web应用程序

    下载demo - 3.9 MB 介绍 这篇文章解释了如何创建一个简单的多语言ASP.NET MVC 5 Web应用程序.该应用程序将能够处理英语(美国),西班牙语和法语.英语将是默认语言.当然,扩展解 ...

  7. LVS+keepalive

    LVS+keepalive 什么是keepalive Keepalived是Linux下一个轻量级别的高可用解决方案.高可用(High Avalilability,HA),其实两种不同的含义:广义来讲 ...

  8. 本文介绍如何使用 Docker Swarm 来部署 Nebula Graph 集群,并部署客户端负载均衡和高可用

    本文作者系:视野金服工程师 | 吴海胜 首发于 Nebula Graph 论坛:https://discuss.nebula-graph.com.cn/t/topic/1388 一.前言 本文介绍如何 ...

  9. linux学习(一)--启动文件bootsect.s

     这是linux由BIOS加载后执行的第一段的启动程序代码,即文件 boot/bootsect.s  首先附图,简单介绍一下从开机加电到第一段linux代码执行的简要过程 1 .globl begte ...

  10. 题解 CF1428A 【Box is Pull】

    通过理解题意,我们发现: 当需要拐弯的时候,兔子需要先走回箱子的位置,再走向拐弯的方向.则拐弯操作的花费为 \(2\) .而直行的操作花费为 \(1\) . 所以, 如果不需要拐弯,也就是 \(x1= ...