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 ...
随机推荐
- 深入了解MySQL主从复制的原理
欢迎微信关注「SH的全栈笔记」 0. 主从复制 首先主从复制是什么?简单来说是让一台MySQL服务器去复制另一台MySQL的数据,使两个服务器的数据保持一致. 这种方式与Redis的主从复制的思路没有 ...
- 真的,kafka 入门看这一篇准没错!
什么是 Kafka Kafka 是一个分布式流式平台,它有三个关键能力 订阅发布记录流,它类似于企业中的消息队列 或 企业消息传递系统 以容错的方式存储记录流 实时记录流 Kafka 的应用 作为消息 ...
- Nginx集成Naxsi防火墙
前言 因工作原因,接触到了WAF,今天部署了一下Naxsi,记录一下 GitHub 正文 环境 Centos 7 下载 更新yum yum update -y 安装必要依赖 yum install g ...
- Python Kafka Client 性能测试
一.前言 由于工作原因使用到了 Kafka,而现有的代码并不能满足性能需求,所以需要开发高效读写 Kafka 的工具,本文是一个 Python Kafka Client 的性能测试记录,通过本次测试, ...
- [CPP] 智能指针
介绍 C++ 的智能指针 (Smart Pointers) 相关 API. C++ 中的智能指针是为了解决内存泄漏.重复释放等问题而提出的,它基于 RAII (Resource Acquisition ...
- 构造无字母数字Webshell
异或: 补充: A的ascii为65,对应二进制是01000001 <?php echo "1"^"A"; ?> 将"A"和&q ...
- 攻防世界 - Misc(一)
base64÷4: 1.下载附件,是一个.txt文件,打开是一串字符, 666C61677B453333423746443841334238343143413936393945444442413234 ...
- os-hackos-3-docker提权
0x00 cewl http://192.168.43.179/websec/爬取页面所有的单词做成字典 hydra -l contact@hacknos.com -P cewl.txt 192.16 ...
- MSDOS(MBR)和GPT磁盘分区表
MBR和GPT分区 MBR分区:以磁盘的第一个扇区(512byte)记录分区表,其中,446byte存储开机管理程序(MBR 主要开机记录),64byte用于存放分区表 分区实际上是对分区表的修改 M ...
- vagrant up报错【io.rb:32:in `encode': "\x95" followed by "\"" on GBK (Encoding::InvalidByteSequenceError)】
vagrant up报错[io.rb:32:in `encode': "\x95" followed by """ on GBK (Encoding: ...