算法详解
模板题
题目描述
如题,已知一个数列,你需要进行下面两种操作:
将某一个数加上 x求出某区间每一个数的和
输入格式
第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:
1 x k 含义:将第 x 个数加上 k
2 x y 含义:输出区间 [x,y] 内每个数的和
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
输入 #1
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
输出 #1
14
16
#include
using namespace std;
#define debug cout<<"OK"<
typedef long long ll;
const int maxn=1e7+1;
const int mod=1e9+7;
int n,m,q1,q2,q3,a;
ll tree[maxn];
inline int lowbit(int x){return x&(-x);}
void update(int x,int k)//树的维护
{
for(;x<=n;x+=lowbit(x))
tree[x]+=k;
}
//此处可以如是想:lowbit取出的是当前x的最低含一位
//权值位,相加后等于向高位进位,并且已有的数位永远为零
//这就可以推出:每当x值+=lowbit(x)时,都会有进位,并且
//进位后的新x值一定包含所有原来的x值,也就是说,这一步
//充分地向上进位,达到区间和更新的目的。
void build()//建树
{
for(int i=1;i<=n;i++)
{
cin>>a;
update(i,a);
}
}
ll query(int x)//查询
{
ll ans=0;
for(;x;x-=lowbit(x))
ans+=tree[x];
return ans;
}
inline ll unite(int x,int y)//求和
{
return query(x)-query(y-1);
}
int main()
{
cin>>n>>m;
build();
for(int i=1;i<=m;i++)
{
cin>>q1>>q2>>q3;
if(q1==1)
update(q2,q3);
else cout<<unite(q3,q2)<<endl;
}
return 0;
}
这种题一般数据较大,所以要用离散化优化
数据过大就用离散化处理,这样把数据转化为1-n的数来代表原来的大小顺序
#include
using namespace std;
const int maxn=5e5+10;
int n,ans,a[maxn],b[maxn];
bool cmp(int l,int r)
{
return a[l]<a[r];
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],b[i]=i;//b数组记录编号
stable_sort(b+1,b+1+n,cmp);//这样排序如果遇见数值相同就不会变换顺序
for(int i=1;i<=n;i++)
a[b[i]]=i;//离散化处理
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
输入:
5
100000 10000 1000 100 10
输出:
5 4 3 2 1
首先要知道什么是逆序对:对于一个数组,如果i > j && a[i] < a[j]
,这两个数就算一对逆序对,简单来说,所有逆序对的个数和就是找每一个数的前面有几个比他的大的数,他们加起来的和就是逆序对的总数。
P1774 最接近神的人
题目描述
破解了符文之语,小FF开启了通往地下的道路。当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某种活动的图案。而石门上方用古代文写着“神的殿堂”。小FF猜想里面应该就有王室的遗产了。但现在的问题是如何打开这扇门……仔细研究后,他发现门上的图案大概是说:古代人认为只有智者才是最容易接近神明的。而最聪明的人往往通过一种仪式选拔出来。仪式大概是指,即将隐退的智者为他的候选人写下一串无序的数字,并让他们进行一种操作,即交换序列中相邻的两个元素。而用最少的交换次数使原序列变成不下降序列的人即是下一任智者。小FF发现门上同样有着n个数字。于是他认为打开这扇门的秘诀就是找到让这个序列变成不下降序列所需要的最小次数。但小FF不会……只好又找到了你,并答应事成之后与你三七分……
输入格式
第一行为一个整数n,表示序列长度
第二行为n个整数,表示序列中每个元素。
输出格式
一个整数ans,即最少操作次数。
输入输出样例
输入 #1
4
2 8 0 3
输出 #1
3
思路:本题要求的是最少需要交换几次,可以用归并排序,亦可用树状数组来做。具体思路链接
#include
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
int n,a[maxn],b[maxn],tree[maxn];
ll ans;
int lowbit(int x){return x&-x;}
bool cmp(int l,int r)//重载运算符用于离散化
{
return a[l]<a[r];
}
void update(int x,int k)//维护树状数组
{
for(;x<=n;x+=lowbit(x))
tree[x]+=k;
}
ll query(int x)//询问
{
ll sum=0;
for(;x;x-=lowbit(x))
sum+=tree[x];
return sum;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i],b[i]=i;
stable_sort(b+1,b+1+n,cmp);
for(int i=1;i<=n;i++)
a[b[i]]=i;
for(int i=1;i<=n;i++)
{
update(a[i],1);
ans+=i-query(a[i]);//用总个数-小于他的数的个数(非逆序对)=逆序对数
}
cout<<ans<<endl;
return 0;
}
P1428 小鱼比可爱
题目描述
人比人,气死人;鱼比鱼,难死鱼。小鱼最近参加了一个“比可爱”比赛,比的是每只鱼的可爱程度。参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数值,表示这只鱼的可爱程度,很显然整数越大,表示这只鱼越可爱,而且任意两只鱼的可爱程度可能一样。由于所有的鱼头都朝向左边,所以每只鱼只能看见在它左边的鱼的可爱程度,它们心里都在计算,在自己的眼力范围内有多少只鱼不如自己可爱呢。请你帮这些可爱但是鱼脑不够用的小鱼们计算一下。
输入格式
第一行输入一个整数 n,表示鱼的数目。
第二行内输入 n 个整数,用空格间隔,依次表示从左到右每只小鱼的可爱程度。
输出格式
行内输出 n 个整数,用空格间隔,依次表示每只小鱼眼中有多少只鱼不如自己可爱。
输入输出样例
输入 #1
6
4 3 0 5 1 2
输出 #1
0 0 0 3 1 2
用离散化就会WA,待解决
#include
using namespace std;
typedef long long ll;
const ll maxn=1000;
int n,tree[maxn],a[maxn],b[maxn];
int lowbit(int x)
{
return x&-x;
}
void update(int x)
{
for(;x<=100;x+=lowbit(x))
tree[x]++;
}
int sum(int x)
{
int s=0;
for(;x;x-=lowbit(x))
s+=tree[x];
return s;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
cout<<sum(a[i])<<" ";
update(a[i]+1);
}
cout<<endl;
return 0;
}
思路
注:如果您通过本文,有(qi)用(guai)的知识增加了,请您点个赞再离开,如果不嫌弃的话,点个关注再走吧,日更博主每天在线答疑 ! 当然,也非常欢迎您能在讨论区指出此文的不足处,作者会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧