P1908 逆序对——树状数组&离散化&快读快写の学习
题目简述:
对于给定的一段正整数序列,逆序对就是序列中 a_i>a_jai>aj 且 i<ji<j 的有序对。
输出序列中逆序对的数目。
知识补充:
树状数组:
这东西就是就是用数组来模拟树形结构,在解决区间上的更新以及求和问题时速度为O(logn),速度比普通数组要快很多)
很重要的一点,那就是:在写代码的时候,把树状数组当成一个普通数组来思考,千万不要将树状数组计算的过程带入思考过程,不然搅死你。
1.单点修改&区间查询
单点增加(初始化):题目:https://www.luogu.com.cn/problem/P3374
void add(int a,int b)
{
for(;a<=n;a+=a&-a) c[a]+=b;
}
区间查询:
int ask(int a)
{
int sum=0;
for(;a;a-=a&-a) sum+=c[a];
return sum;
}
上代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int c[500005];
int d,e,f;
void add(int a,int b)
{
for(;a<=n;a+=a&-a) c[a]+=b;
} int ask(int a)
{
int sum=0;
for(;a;a-=a&-a) sum+=c[a];
return sum;
} int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int k;
cin>>k;
add(i,k);
}
for(int i=1;i<=m;i++)
{
cin>>d>>e>>f;
if(d==1) add(e,f);
if(d==2) cout<<tot(f)-tot(e-1)<<endl;
}
return 0;
}
2.区间修改&单点查询
这里要用到差分的思想,总体思想是将单点查询转化为差分的区间查询,区间的增加转化为差分的变化。
与上一种不同的是,这里需要一个普通数组来计算差。
上代码:题目:https://www.luogu.com.cn/problem/P3368
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long del[500005];//差分的树状数组
long long ch[500005];//初始数组,其实只有在输入算差的时候才有一点用处
int o,x,y,k;
void add(int a,int b)//区间增加
{
for(;a<=n;a+=a&-a) del[a]+=b;
return ;
}
void chafen(int a,int b,int c)//差分的思想,先将【a,n】的所有差+c,再将【b+1,n】的所有差-c
{
add(a,c);
add(b+1,-c);
}
int sum(int a)//单点查询
{
int ans=0;
for(;a;a-=a&-a) ans+=del[a];
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>ch[i];
add(i,ch[i]-ch[i-1]);//初始化
}
for(int i=1;i<=m;i++)
{
cin>>o;
if(o==1)
{
cin>>x>>y>>k;
chafen(x,y,k);
}
if(o==2)
{
cin>>k;
cout<<sum(k)<<endl;
}
}
return 0;
}
3.区间修改&区间查询(还没学呢~)
看了一下午,总算会了(bushi
主要还是我太菜了,看不懂网上大佬们专业的数学表示方法。
来推导一下过程:
(D[i]:用于存储前i项差分的树状数组。)
A[1]=D[1]
A[2]=D[1]+D[2]
A[3]=D[1]+D[2]+D[3]
。。。。。。。。。。。
A[i]=D[1]+D[2]+...+D[i]
相加,可以得到:
A[1]+A[2]+A[3]+...A[i]=D[1]*i+D[2]*(i-1)+...D[i]*1;
=n(D[1]+D[2]+D[3]+...D[i])-(D[1]*0+D[2]*1+D[3]*2+...D[i]*(i-1))
不难发现,除了D[i],我们还需要记录D[i]*(i-1),就能求出前缀和了。
值得注意的是,在add和ask操作中,一开始要先记录下n的大小,因为n的大小在每一次操作中是固定的。
上代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int sum[N];
int sum1[N] ;
int n1;
int l,r;
int k;
int a[N];
void add(int n,int k){
int i=n;//记录 n值
for(;n<=n1;n+=n&-n){
sum[n]+=k;
sum1[n]+=k*(i-1);
}
}
int ask(int n){
int ans=0;
int i=n;//记录n值
for(;n>0;n-=n&-n){
ans+=sum[n]*i-sum1[n];
}
return ans;
}
int main(){
cin>>n1;
for(int i=1;i<=n1;i++){
cin>>a[i];
add(i,a[i]-a[i-1]);
}
add(l,k);
add(r,-k);//区间修改
cin>>l>>r;
cout<<ask(r)-ask(l-1); //区间查询
}
下一步就是学线段树了,冲冲冲!
离散化
这种东西有什么用呢?举个例子吧:
10 100 1000用桶储存起来,问比1000小的有多少个。
1 2 3的结果会与上面那组数据相同,但他只找两次,而上边要找989次。
相当于减少了一些无用功。
直接看代码吧(记住就行):
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int lsh[1000], lshcopy[1000], sy[1000]; //lsh[n]是即将被离散化的数组,lshcopy[n]是a[n]的副本,sy[n]用于排序去重后提供离散化后的值
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&sy[i]);
lshcopy[i]=sy[i]; }
sort(sy,sy+n);//第一步排序
for(int i=0;i<n;i++)
{
cout<<'('<<sy[i]<<')';
cout<<"\n";
}
int size=unique(sy,sy+n)-sy;//unique显示去重后的个数
printf("size is : %d",size);
printf("\n");
for(int i=0;i<n;i++)
{
lsh[i]=lower_bound(sy,sy+size,lshcopy[i])-sy; //即lsh[i]为lshcopy[i]离散化后对应的值
printf("lsh is : %d",lsh[i]);
}
}
快读快写:
快读:
int read(){
int w=0;bool f=0;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)){w=(w<<3)+(w<<1)+ch-'0';ch=getchar();}
w=f?-w:w;
return w;
}
快写:
void write(long long x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)write(x/10);
putchar(x%10+'0');
return;
}
题目分析:传送门:https://www.luogu.com.cn/problem/P1908
维护一个树状数组,用于维护当前各个数字的出现次数。
for example:
6
5 4 2 6 3 1
注意!要从后向前!
1 2 3 4 5 6
1 ————此时1前面,没有任何的数,ans=0;
1 2 3 4 5 6
1 1 ————这里3,6已经·可以组成一个逆序对了,利用区间查询ask函数可以算出来。
后同理。
记得离散化就行。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
long long tree[N];
int s[N];
int scopy[N];
int ls[N];
int n;
long long ans=0;
int read(){
int w=0;bool f=0;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)){w=(w<<3)+(w<<1)+ch-'0';ch=getchar();}
w=f?-w:w;
return w;
}
void write(long long x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)write(x/10);
putchar(x%10+'0');
return;
}
void add(int a,int b)
{
for(;a<=N-10;a+=a&-a) tree[a]+=b;
} long long ask(int a)
{
long long tot=0;
for(;a;a-=a&-a)tot+=tree[a];
return tot;
} int main()
{
n=read();
for(int i=1;i<=n;i++)
{
s[i]=read();
scopy[i]=s[i];
} sort(s+1,s+n+1);
int size=unique(s+1,s+n+1)-s;
for (int i=1;i<=n;i++)
{
ls[i]=lower_bound(s+1,s+size+1,scopy[i])-s;
} for(int i=n;i>=1;i--)
{
add(ls[i],1);
ans+=ask(ls[i]-1);
}
write(ans);
return 0;
}
完结撒花~
P1908 逆序对——树状数组&离散化&快读快写の学习的更多相关文章
- 洛谷 P1908 逆序对(树状数组解法)
归并排序解法:https://www.cnblogs.com/lipeiyi520/p/10356882.html 题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不 ...
- luogu P1908 逆序对 |树状数组
题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计.最近,TOM老猫查阅到一个人类称之为"逆序对"的 ...
- [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)
[BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...
- Bzoj 2141: 排队 分块,逆序对,树状数组
2141: 排队 Time Limit: 4 Sec Memory Limit: 259 MBSubmit: 1310 Solved: 517[Submit][Status][Discuss] D ...
- 求逆序对[树状数组] jdoj
求逆序对 题目大意:给你一个序列,求逆序对个数. 注释:n<=$10^5$. 此题显然可以跑暴力.想枚举1到n,再求在i的后缀中有多少比i小的,统计答案即可.这显然是$n^2$的.这...显然过 ...
- Ultra-QuickSort---poj2299 (归并排序.逆序数.树状数组.离散化)
题目链接:http://poj.org/problem?id=2299 题意就是求把数组按从小到大的顺序排列,每次只能交换相邻的两个数, 求至少交换了几次 就是求逆序数 #include<std ...
- luogu1908 逆序对 树状数组
题目大意:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对.求一段序列的逆序对数. 对于一个数组T,其一个点的值为值与该点下标相等的A序列中点的个数.对T维护一个树状数 ...
- 【a703】求逆序对(树状数组的解法)
Time Limit: 10 second Memory Limit: 2 MB 问题描述 给定一个序列a1,a2...an.如果存在i小于j 并且ai大于aj,那么我们称之为逆序对,求给定序列中逆序 ...
- BZOJ - 3295 动态逆序对 (树状数组套treap)
题目链接 思路和bzoj2141差不多,不过这道题的数据更强一些,线段树套treapT了,树状数组套treap卡过~~ #include<bits/stdc++.h> using name ...
随机推荐
- Apache本机不同端口多站点配置:httpd-vhosts.conf(转载)
环境:Apache2.2.9,Resin-3.1.6,Win Server 2003 1.解压Resin至任意目录,我的是D:; 2. 安装Apache,具体操作下一步.下一步即可,其中要配置的地方是 ...
- ssh问题之复盘
一.问题发生.排查以及解决 某天H博士在登录B服务器时发现一个严重的问题,问题是H博士在执行脚本出现一个异常,这个异常是过去我执行脚本只需输入一次密码,现在要输入五六次,只有输入五六次后才能正确执行完 ...
- shell实现99乘法表
#!/usr/bin/env sh for ((x=1;x<10;x++));do for ((y=1;x>=y;y++));do printf "${y}x${x}=$(exp ...
- (十五)xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要 ...
- Java高并发与多线程(二)-----线程的实现方式
今天,我们开始Java高并发与多线程的第二篇,线程的实现方式. 通常来讲,线程有三种基础实现方式,一种是继承Thread类,一种是实现Runnable接口,还有一种是实现Callable接口,当然,如 ...
- 【ORACLE错误】SP2-0618: Cannot find the Session Identifier. Check PLUSTRACE role is enabled
执行set autotrace traceonly的时候,报错 SQL> set autotrace traceonly SP2-0618: Cannot find the Session Id ...
- xray—学习笔记
长亭xray扫描器 简介 xray (https://github.com/chaitin/xray) 是从长亭洞鉴核心引擎中提取出的社区版漏洞扫描神器,支持主动.被动多种扫描方式,自备盲打平台.可以 ...
- three.js cannon.js物理引擎之约束
今天郭先生继续说cannon.js,主演内容就是点对点约束和2D坐标转3D坐标.仍然以一个案例为例,场景由一个地面.若干网格组成的约束体和一些拥有初速度的球体组成,如下图.线案例请点击博客原文. 下面 ...
- hello2 部分代码解析
ResponseServlet.java源码文件 1 @WebServlet("/response") //以@WebServlet注释开头,注释指定相对于上下文根的URL模式, ...
- IE浏览器兼容问题总结
IE浏览器兼容问题总结 引自掘金:https://juejin.cn/post/6844903825854185480 一.标准盒模型和怪异盒模型 浏览器的盒子模型分为两类: 标准的W3C盒子模型. ...