线段树 用于处理动态范围最小值问题。
此处给出的 “点修改 区间查找” 是 最基本的内容。
首先给出模板:(此处给的是储存最大值)
#define ls o<<1 //为方便写程序,使用了 ls 代替 左子树的编号,rs 代替 右子树的编号
#define rs o<<1|1
#define root 1,1,n //用root 表示 编号为1的节点,左边界1,右边界n
int f[MAXN*4];//线段树需要存储的空间为 区间长度的4倍
void build(int o,int l,int r)//构造树
{
if(l == r) scanf("%d",&f[o]);
else
{
int m = l + (r-l)/2;
build(ls,l,m);
build(rs,m+1,r);
f[o] = max(f[ls],f[rs]);
}
}
int query(int ql,int qr,int o,int l,int r)//查找
{
if(ql > r || qr < l) return -inf;
if(l >= ql && r <= qr) return f[o];
int ans = -inf,m = l + (r-l)/2;
ans = max(ans,query(ql,qr,ls,l,m));
ans = max(ans,query(ql,qr,rs,m+1,r));
return ans;
}
void update(int q,int v,int o,int l,int r)//更新节点
{
if(l==r) {f[o] = v;return;}
int m = l+(r-l)/2;
if(q <= m) update(q,v,ls,l,m);
else update(q,v,rs,m+1,r);
f[o] = max(f[ls],f[rs]);
}
hdu 4302(点击进入):老鼠吃东西(我也不知道是不是老鼠,反正是个动物),有很多做法,此处使用线段树。
当前所在位置now,他要找最近的位置,那么就是搜索now的右边区间最左边的点,和now的左边区间最右边的点。然后比较搜到的两个点。
由于有一个是查找最左,一个查找最右,所以写了两个查找函数。看起里代码长,实际上都是复制了一遍。当然,只写一个查找函数也是可以实现的。
另外,我当时写的时候把所有点右移一格,实际上没有必要,懒得改。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 100001;
const int inf = 0x3f3f3f3f;
const int M = 11;
const int eps = -(1<<29);
int now,f[4*MAXN],dir;//dir表示老鼠面对的方向,此题f数组储存的是区间内食物的数量
void update(int p,int v,int o,int l,int r)
{
int m = l + (r-l)/2;
f[o] += v;
if(l==r)return;
if(p <= m) update(p,v,2*o,l,m);
else update(p,v,2*o+1,m+1,r);
}
int query(int ql,int qr,int o,int l,int r)
{
if(ql > r || qr < l) return -inf;//判断无效区间
if(!f[o]) return -inf;//判断有无食物
if(l==r) return l;
int m = l+(r-l)/2,ans;
ans = query(ql,qr,o*2+1,m+1,r);
if(ans != -inf)return ans;
ans = query(ql,qr,o*2,l,m);
return ans;
}
int query2(int ql,int qr,int o,int l,int r)
{
if(ql > r || qr < l) return inf;//判断无效区间
if(!f[o]) return inf;//判断有无食物
if(l==r) return l;
int m = l+(r-l)/2,ans;
ans = query2(ql,qr,o*2,l,m);
if(ans != inf)return ans;
ans = query2(ql,qr,o*2+1,m+1,r);
return ans;
}
int main()
{
// fre();
int t,L,n,a,b,lto,rto,ans,kase = 0;;
scanf("%d",&t);
while(t--)
{
dir = 1; now = 1;
ans = 0;
MS(f,0);//init;
scanf("%d%d",&L,&n);//input
++L;//所有坐标左移一格
while(n--)//solve
{
scanf("%d",&a);
if(a==0)
{
scanf("%d",&b);
update(b+1,1,1,1,L);//由于左移一格,因此b+1
}
if(a==1)
{
lto = query(1,now,1,1,L);
rto = query2(now,L,1,1,L);
if(lto == -inf && rto == inf) continue;
if(abs(lto-now) < abs(rto-now) || (abs(lto-now) == abs(rto-now) && dir == 0))
{
ans += abs(lto-now);
now = lto;
dir = 0;
}
else if(abs(lto-now) > abs(rto-now) || (abs(lto-now) == abs(rto-now) && dir == 1))
{
ans += abs(rto-now);
now = rto;
dir = 1;
}
update(now,-1,1,1,L);
}
}
printf("Case %d: %d\n",++kase,ans);
}
}
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,1,n
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 200001;
const int inf = 0x3f3f3f3f;
int f[MAXN*4];
void build(int o,int l,int r)
{
if(l == r) scanf("%d",&f[o]);
else
{
int m = l + (r-l)/2;
build(ls,l,m);
build(rs,m+1,r);
f[o] = max(f[ls],f[rs]);
}
}
int query(int ql,int qr,int o,int l,int r)
{
if(ql > r || qr < l) return -inf;
if(l >= ql && r <= qr) return f[o];
int ans = -inf,m = l + (r-l)/2;
ans = max(ans,query(ql,qr,ls,l,m));
ans = max(ans,query(ql,qr,rs,m+1,r));
return ans;
}
void update(int q,int v,int o,int l,int r)
{
if(l==r) {f[o] = v;return;}
int m = l+(r-l)/2;
if(q <= m) update(q,v,ls,l,m);
else update(q,v,rs,m+1,r);
f[o] = max(f[ls],f[rs]);
}
int main()
{
// fre();
int n,m,ql,qr;
char s[5];
while(~scanf("%d%d",&n,&m))
{
build(root);
while(m--)
{
scanf("%s",s);
scanf("%d%d",&ql,&qr);
if(s[0]=='Q') printf("%d\n",query(ql,qr,root));
else update(ql,qr,root);
}
}
}
hdu 1394(点击打开): 实际上这里求逆序数用树状数组更快。为了练习仍使用线段树计算原序列的逆序数。
然后可以递推求出其他的逆序数。 每次把最前面一个移到最后面,假设移动的数的值是x,总共n个数,则逆序数 的改变量为n-2*x-1。(容易证明)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,0,n-1
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 5001;
const int inf = 0x3f3f3f3f;
int f[MAXN*4];
int query(int ql,int qr,int o,int l,int r)
{
if(ql > r || qr < l) return 0;
if(l >= ql && r <= qr) return f[o];
int ans = 0,m = l + (r-l)/2;
ans += query(ql,qr,ls,l,m);
ans += query(ql,qr,rs,m+1,r);
return ans;
}
void update(int q,int o,int l,int r)
{
f[o]++;
if(l==r) return;
int m = l+(r-l)/2;
if(q <= m) update(q,ls,l,m);
else update(q,rs,m+1,r);
}
int main()
{
// fre();
int n,sum,a[MAXN],ans;
int i;
while(~scanf("%d",&n))
{
sum = 0;
MS(f,0);//init
for(i = 0; i < n; ++i)//input 求逆序数
{
scanf("%d",&a[i]);
sum +=query(a[i]+1,n-1,root);
update(a[i],root);
}
ans = sum;
for(i = 0; i < n; ++i)//solve 递推求解
{
sum += n-2*a[i]-1;
if(sum < ans) ans = sum;
}
printf("%d\n",ans);
}
}
hdu 2795(点击打开): 数据 h 范围很大,数组存不下。但是发现n最多200000,h超过n是没必要的(想想为什么),所以如果h大于n可以直接另h = n。
f数组储存区间内剩余宽度 w的最大值
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#define MS(x,y) memset(x,y,sizeof(x))
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define root 1,1,h
using namespace std;
void fre(){freopen("t.txt","r",stdin);}
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 200005;
const int inf = 0x3f3f3f3f;
int f[MAXN*4],n,h,w;
void build(int o,int l,int r)//初始状态,全都是剩余w。 其实可以不用这个函数,用循环就可以了。
{
f[o] = w;
if(l==r)return;
int m = l+(r-l)/2;
build(ls,l,m);
build(rs,m+1,r);
}
int query(int v,int o,int l,int r)//查找最左边的满足条件的值
{
if(f[o] < v) return -1;
if(l==r) return l;
int m = l+(r-l)/2,ans;
ans = query(v,ls,l,m);
if(ans == -1) ans = query(v,rs,m+1,r);
return ans;
}
void update(int q,int v,int o,int l,int r)
{
if(l==r) {f[o]-=v;return;}
int m=l+(r-l)/2,ans = -inf;
if(q <= m) update(q,v,ls,l,m); else update(q,v,rs,m+1,r);
f[o] = max(f[ls],f[rs]);
}
int main()
{
//fre();
int ans,ww;
int i;
while(~scanf("%d%d%d",&h,&w,&n))
{
if(h > n) h = n;//优化!
build(root);//init
while(n--)//solve
{
scanf("%d",&ww);
ans = query(ww,root);
if(ans!=-1)update(ans,ww,root);
printf("%d\n",ans);
}
}
}