Acmer可怜啊,根本没有休息,昨天才刚刚完成了矩阵专题,今天又要开线段树专题了。唉,等我以后月薪15K的时候,我要好好享受人生。。。。。。呃,扯远了。线段树是一个非常重要的数据结构,以前就学习过,但是没有系统的刷过难题,这次我决定将kuangbin先生的专题和NotOnlySuccess大神的专题一起刷掉。因为题目多又难,所以分成几个部分(最多三个把)。
对于线段树的话,主要是理解它的树形结构吧,推荐和树状数组一起学习。似乎树状数组就是线段树的退化,树状数组节约了差不多一半的空间,但是必须满足“加法原则”(我不清楚的,说错了别打我)。理解的话,我没有什么比较好的资料,但是模版的话,强烈推荐NotOnlySuccess大神的模版,写得非常非常好。
第一题 HDU 1166
分析:线段树的入门模版题。求区间和+点修改
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 50000+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
c=getchar();
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
//--------------------------------------------------
int sum[maxn<<2];
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
scanf("%d",&sum[rt]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int l,int r,int rt,int p,int add){
if(l==r){
sum[rt]+=add;
return ;
}
int m=(l+r)>>1;
if(p<=m)update(lson,p,add);
else update(rson,p,add);
pushup(rt);
}
int query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R){
return sum[rt];
}
int m=(l+r)>>1;
int ans=0;
if(L<=m)ans+=query(lson,L,R);
if(R>m)ans+=query(rson,L,R);
return ans;
}
int main(){
//freopen("d:\\acm\\in.in","r",stdin);
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
int n;
scanf("%d",&n);
build(1,n,1);
printf("Case %d:\n",cas);
char op[10];
while(1){
scanf("%s",op);
if(op[0]=='E')break;
int a,b;
scanf("%d %d",&a,&b);
if(op[0]=='A')update(1,n,1,a,b);
else if(op[0]=='S')update(1,n,1,a,-b);
else cout<<query(1,n,1,a,b)<<endl;
}
}
return 0;
}
分析:和前面那道题相差不大,求区间和变成求区间最大值。+点修改
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <fstream>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define maxn 200000+10
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define clr(x,y) memset(x,y,sizeof(x))
#define rep(i,n) for(int i=0;i<(n);i++)
#define repf(i,a,b) for(int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define mp make_pair
#define FI first
#define SE second
#define IT iterator
#define PB push_back
#define Times 10
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const double eps = 1e-10;
const double pi = acos(-1.0);
const ll mod = 1e9+7;
const int inf = 0x3f3f3f3f;
inline void RI(int& x)
{
x=0;
char c=getchar();
while(!((c>='0'&&c<='9')||c=='-'))c=getchar();
bool flag=1;
if(c=='-')
{
flag=0;
c=getchar();
}
while(c<='9'&&c>='0')
{
x=x*10+c-'0';
c=getchar();
}
if(!flag)x=-x;
}
//--------------------------------------------------
int maxx[maxn<<2];
void pushup(int rt){
maxx[rt]=max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l==r){
scanf("%d",&maxx[rt]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int l,int r,int rt,int p,int xx){
if(l==r){
maxx[rt]=xx;
return ;
}
int m=(l+r)>>1;
if(p<=m)update(lson,p,xx);
else update(rson,p,xx);
pushup(rt);
}
int query(int l,int r,int rt,int L,int R){
if(L<=l&&r<=R){
return maxx[rt] ;
}
int m=(l+r)>>1;
int ans=0;
if(L<=m)ans=max(ans,query(lson,L,R));
if(R>m)ans=max(ans,query(rson,L,R));
return ans;
}
int main(){
//freopen("d:\\acm\\in.in","r",stdin);
int n,m;
while(~scanf("%d %d",&n,&m)){
build(1,n,1);
char op[10];
int a,b;
while(m--){
scanf("%s %d %d",op,&a,&b);
if(op[0]=='Q')cout<<query(1,n,1,a,b)<<endl;
else update(1,n,1,a,b);
}
}
return 0;
}
分析:这是一道比较坑的题目。这道题目的意思是,给你N个数,这N个数分别是0到N-1。数列是乱序给的,让你求出它的逆序对数。然后每次将第一个放到最后一个,一次循环,这样一共可以得到N个数列,让你求出所有数列中的最小逆序对数。
那么这道题的解题思路是先求出第一个数列的逆序对数,然后可以根据前一个数列的逆序对数和数列的第一个数循环的求出下一个数列的逆序对数。
不妨设前一个数列逆序对数为ank,数列第一个数为Ai,那么下一个逆序对数为ank - Ai +n - Ai -1,我觉得应该很好想。
然后问题来了,第一个数列的逆序对数怎么求?
这里提供两种方法: