1.hud 1166 http://acm.hdu.edu.cn/showproblem.php?pid=1166 敌兵布阵
本题目涉及到了区间操作和点操作两种操作。
区间操作(区间和):我们采用了递推的方法来建树,建好了叶子结点之后,我们再回溯来建整个区间。(这个操作是关键)
点操作:点操作必然涉及到区间的改动,我们判定,只要这个点在一个区间上,那么我们就对这个区间操作。
View Code
#include "iostream"
#include "string"
#include "algorithm"
using namespace std;
#define maxn 50005
int len, peo[maxn], ans;
typedef struct node
{
int l, r, m;
int s;
}node;
node t[maxn*4];
void build(int ll, int le, int ri)
{
t[ll].l = le;
t[ll].r = ri;
t[ll].m = (le+ri)>>1;
if(le==ri) t[ll].s = peo[le];
else
{
build(ll<<1, le, t[ll].m);
build(ll<<1|1, t[ll].m+1, ri);
t[ll].s = t[ll<<1].s + t[ll<<1|1].s;
}
}
void query(int ll, int le, int ri)
{
if(le<=t[ll].l && ri>=t[ll].r) ans+=t[ll].s;
else
{
if(le>t[ll].m) query(ll<<1|1, le, ri);
else if(ri<=t[ll].m) query(ll<<1, le, ri);
else
{
query(ll<<1, le, t[ll].m);
query(ll<<1|1, t[ll].m+1, ri);
}
}
}
void add(int ll, int p, int v)
{
if(p>=t[ll].l && p<=t[ll].r) t[ll].s += v;
if(t[ll].l==p && t[ll].r==p) return;
if(p>t[ll].m) add(ll<<1|1, p, v);
else if(p<=t[ll].m) add(ll<<1, p, v);
}
void sub(int ll, int p, int v)
{
if(p>=t[ll].l && p<=t[ll].r) t[ll].s -= v;
if(t[ll].l==p && t[ll].r==p) return;
if(p>t[ll].m) sub(ll<<1|1, p, v);
else if(p<=t[ll].m) sub(ll<<1, p, v);
}
int main()
{
int i, j, C, le, ri;
char op[10];
scanf("%d", &C);
for(i=1; i<=C; i++)
{
printf("Case %d:\n", i);
scanf("%d", &len);
for(j=1; j<=len; j++) scanf("%d", peo+j);
build(1, 1, len);
while(scanf("%s", op)!=EOF)
{
if(strcmp(op, "End")==0) break;
scanf("%d%d", &le, &ri);
if(strcmp(op, "Query")==0)
{
ans = 0;
query(1, le, ri);
printf("%d\n", ans);
}
else if(strcmp(op, "Add")==0)
{
add(1, le, ri);
}
else if(strcmp(op, "Sub")==0)
{
sub(1, le, ri);
}
}
}
return 0;
}
2.hdu 1754 http://acm.hdu.edu.cn/showproblem.php?pid=1754 I hate it
本题目涉及到了区间操作和点操作两种操作。
区间操作(区间最值):平常的建树。
点操作:我们每向区间插入一个点就和当前区间的最大值比较一下,如果大于最大值,则更新。(这个操作是关键)
View Code
#include "iostream"
#include "string"
#include "cstdio"
#include "cstring"
#include "algorithm"
using namespace std;
#define maxn 200005
typedef struct node
{
int l, r, m, ma;
}node;
int ans;
node t[maxn<<2];
void Build(int ll, int l, int r)
{
t[ll].ma=-1;
t[ll].l=l;
t[ll].r=r;
t[ll].m=(l+r)>>1;
if(l!=r)
{
Build(ll<<1, l, t[ll].m);
Build(ll<<1|1, t[ll].m+1, r);
}
}
void Insert(int ll, int p, int v)
{
if(p>=t[ll].l && p<=t[ll].r && t[ll].ma<v) t[ll].ma=v;
if(p==t[ll].l && p==t[ll].r) return;
if(p<=t[ll].m) Insert(ll<<1, p, v);
else if(p>t[ll].m) Insert(ll<<1|1, p, v);
}
int Find(int ll, int l, int r)
{
if(l==t[ll].l && t[ll].r==r) return t[ll].ma;
if(r<=t[ll].m) Find(ll<<1, l, r);
else if(l>t[ll].m) Find(ll<<1|1, l, r);
else
{
int a=Find(ll<<1, l, t[ll].m);
int b=Find(ll<<1|1, t[ll].m+1, r);
return a>b?a:b;
}
}
int main()
{
int n, op, i, j, v, l, r;
char opp[10];
while(scanf("%d%d", &n, &op)!=EOF)
{
Build(1, 1, n);
for(i=1; i<=n; i++)
{
scanf("%d", &v);
Insert(1, i, v);
}
for(i=0; i<op; i++)
{
scanf("%s%d%d", opp, &l, &r);
if(strcmp(opp, "Q")==0)
{
printf("%d\n", Find(1, l, r));
}
else if(strcmp(opp, "U")==0)
{
Insert(1, l, r);
}
}
}
}
3.hdu 1698http://acm.hdu.edu.cn/showproblem.php?pid=1698 Just a hook
本题只涉及到了区间操作。
区间操作(全区间键值的改变):在维护更新的时候我们将无用的区间屏蔽掉,这样就可以大大提高时间效率。如何屏蔽?这个是关键。
View Code
#include "iostream"
#include "string"
#include "cstdio"
#include "cstring"
#include "algorithm"
using namespace std;
#define maxn 100005
#define L(x) (x<<1)
#define R(x) (x<<1|1)
typedef struct node
{
int l, r, z;
}node;
node t[maxn<<2];
void Build(int ll, int l, int r)
{
t[ll].l=l;
t[ll].r=r;
t[ll].z=1;
if(l<r)
{
int m=(t[ll].l+t[ll].r)>>1;
Build(L(ll), l, m);
Build(R(ll), m+1, r);
}
}
void Update(int ll, int l, int r, int z)
{
if(l==t[ll].l && t[ll].r==r)
{
t[ll].z=z;//更新
return;
}
if(t[ll].z>0)//只要不是刚好的区间,我们就往下更新,同时将这一层的区间屏蔽掉,说具体点就是t[ll].z=-1
{
t[R(ll)].z = t[ll].z;
t[L(ll)].z = t[ll].z;
t[ll].z=-1;
}
if(t[ll].l==t[ll].r) return;
int m=(t[ll].l+t[ll].r)>>1;
if(r<=m) Update(L(ll), l, r, z);
else if(l>m) Update(R(ll), l, r, z);
else
{
Update(L(ll), l, m, z);
Update(R(ll), m+1, r, z);
}
}
int Query(int ll, int l, int r)
{
if(t[ll].z>0) return (t[ll].r-t[ll].l+1)*t[ll].z;
int m=(t[ll].l+t[ll].r)>>1;
if(r<=m) return Query(L(ll), l, r);
else if(l>m) return Query(R(ll), l, r);
else return Query(L(ll), l, m)+Query(R(ll), m+1, r);
}
int main()
{
int c, n, op, i, l, r, z, j, ans;
scanf("%d", &c);
for(i=1; i<=c; i++)
{
scanf("%d%d", &n, &op);
Build(1, 1, n);
for(j=0; j<op; j++)
{
scanf("%d%d%d", &l, &r, &z);
Update(1, l, r, z);
}
ans=Query(1, 1, n);
printf("Case %d: The total value of the hook is %d.\n", i, ans);
}
}
4.hdu 1394http://acm.hdu.edu.cn/showproblem.php?pid=1394 Minimum Inversion Number
本题是一种类型的题目——逆序数的求法。
本题涉及到了区间操作。
但是本题的区间操作有一定的讲究,也就是一边插入一边查找。不然等插入完了再来查找就不知道是在它前面还是后面出现的了。
其它的就和平常的线段树一样了。
View Code
#include "iostream"
#include "string"
#include "cstring"
#include "algorithm"
#include "cstdio"
using namespace std;
#define maxn 5005
#define L(x) (x<<1)
#define R(x) (x<<1|1)
typedef struct segtree
{
int l, r, s;
}segtree;
segtree t[maxn<<2];
int s[maxn];
void Build(int ll, int l, int r)
{
t[ll].l=l;
t[ll].r=r;
t[ll].s=0;
if(l<r)
{
int m=(l+r)>>1;
Build(L(ll), l, m);
Build(R(ll), m+1, r);
}
}
int Query(int ll, int l, int r)
{
if(t[ll].l==l && t[ll].r==r) return t[ll].s;
int m=(t[ll].l+t[ll].r)>>1;
if(r<=m) return Query(L(ll), l, r);
else if(l>m) return Query(R(ll), l, r);
else return Query(L(ll), l, m)+Query(R(ll), m+1, r);
}
void Update(int ll, int k)
{
t[ll].s++;
if(t[ll].l==t[ll].r) return;
int m=(t[ll].l+t[ll].r)>>1;
if(k<=m) Update(L(ll), k);
else Update(R(ll), k);
}
int main()
{
int n, i, t, ans;
while(scanf("%d", &n)!=EOF)
{
ans=0;
Build(1, 0, n-1);
for(i=0; i<n; i++)//我们来求一下“输入顺序时”的逆序对数
{
scanf("%d", &s[i]);
ans+=Query(1, s[i], n-1);//在s[i]~n-1这个区间中有多少个数是在s[i]之前出现的,那么对于这个数而言就有多少个逆序对数,不是吗?好好想想……
Update(1, s[i]);
}
t=ans;
for(i=0; i<n-1; i++)
{
t=t+(n-1-s[i])-s[i];
if(ans>t) ans=t;
}
printf("%d\n", ans);
}
return 0;
}
5. hdu 2492http://acm.hdu.edu.cn/showproblem.php?pid=2492 Ping pong
本题也是一道逆序数的题目。只不为要求两种逆序都要算出来。也就是说A>B>C和A<B<C两种情况。
其实仔细想想本题我们可以得到一个这样的结论。
如果完全按照题目的描述来想算法,我们是很容易进入误区的,
但是,如果我们将题目的意思也取反来看一下,当然这个取反和原题意是等价的,就很容易知道是什么样的题目了。
View Code
#include "iostream"
#include "string"
#include "cstring"
#include "algorithm"
#include "cstdio"
using namespace std;
#define maxn 20010
#define maxs 100010
#define L(x) (x<<1)
#define R(x) (x<<1|1)
typedef struct segtree
{
int l, r, s;
}segtree;
segtree t[maxs<<2];
int s[maxn];
void Build(int f, int l, int r)
{
t[f].l=l;
t[f].r=r;
t[f].s=0;
if(l<r)
{
int m=(l+r)>>1;
Build(L(f), l, m);
Build(R(f), m+1, r);
}
}
int Query(int f, int l, int r)
{
if(t[f].l==l && t[f].r==r) return t[f].s;
int m=(t[f].l+t[f].r)>>1;
if(r<=m) return Query(L(f), l, r);
else if(l>m) return Query(R(f), l, r);
else return Query(L(f), l, m)+Query(R(f), m+1, r);
}
void Update(int f, int k)
{
t[f].s++;
if(t[f].l==t[f].r) return;
int m=(t[f].l+t[f].r)>>1;
if(k<=m) Update(L(f), k);
else Update(R(f), k);
}
int main()
{
int n, i, T, k;
long long fs[maxn], fb[maxn], ss[maxn], sb[maxn];
scanf("%d", &T);
for(k=0; k<T; k++)
{
scanf("%d", &n);
memset(fs, 0, sizeof fs);
memset(fb, 0, sizeof fb);
memset(ss, 0, sizeof ss);
memset(sb, 0, sizeof sb);
Build(1, 1, maxs);
for(i=0; i<n; i++)//我们来求一下“输入顺序时”的逆序对数
{
scanf("%d", &s[i]);
fb[i]=Query(1, s[i]+1, maxs);//big
if(s[i]==1)
{
Update(1, s[i]);
continue;
}
fs[i]=Query(1, 1, s[i]-1);//在s[i]~n-1这个区间中有多少个数是在s[i]之前出现的,那么对于这个数而言就有多少个逆序对数,不是吗?好好想想……
Update(1, s[i]);
}
Build(1, 1, maxs);
for(i=n-1; i>=0; i--)
{
sb[i]=Query(1, s[i]+1, maxs);
if(s[i]==1)
{
Update(1, s[i]);
continue;
}
ss[i]=Query(1, 1, s[i]-1);//small
Update(1, s[i]);
}
long long ans=0;
for(i=1; i<n-1; i++)
{
ans+=fs[i]*sb[i];
ans+=fb[i]*ss[i];
}
printf("%I64d\n", ans);
}
return 0;
}
6. hdu 3308http://acm.hdu.edu.cn/showproblem.php?pid=3308 LCIS
本题是一种类型的题目,解法有一定的套路。也就是lm,rm,m,len,lv,rv等等,具体是什么含义,看看代码就知道了。
本题涉及到了区间操作和点的操作。
区间操作,这个比较麻烦,因为没有记录左右边界,所以要小心,这只是一个不起眼的小难题。
区间的操作,难就难在up函数,和query函数上,这两个一定要看懂。其它的就好说了。
点操作一般,就是平时的操作。
View Code
#include "iostream"
#include "string"
#include "cstdio"
#include "cstring"
#include "algorithm"
using namespace std;
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define maxn 100005
typedef struct node
{
int lm, rm, m;
int lv, rv, len;
}node;//我们并没有记录l, r
node t[maxn<<2];
void Up(node &f, node &l, node &r)
{
f.lv = l.lv;
f.rv = r.rv;
if(l.rv<r.lv)//是递增的
{
f.lm=(l.lm==l.len?l.lm+r.lm:l.lm);//左边的最值就是它的区间长度那么……否则……
f.rm=(r.rm==r.len?r.rm+l.rm:r.rm);//右边的最值就是它的区间长度那么……否则……
f.m=max(max(f.lm, f.rm), l.rm+r.lm);//l.rm+r.lm表示不包含左右端点的值
f.m=max(max(l.m, r.m), f.m);
}
else//如果不是递增的
{
f.lm=l.lm;
f.rm=r.rm;
f.m=max(l.m, r.m);
}
}
void Build(int f, int l, int r)
{
t[f].len=r-l+1;
if(l==r)
{
scanf("%d", &t[f].lv);
t[f].rv = t[f].lv;
t[f].lm = t[f].rm = t[f].m = 1;
return;
}
int m=(l+r)>>1;
Build(L(f), l, m);
Build(R(f), m+1, r);
//当建树完成的时候我们要进行统计运算,也就是向上计算
Up(t[f], t[L(f)], t[R(f)]);
}
void Update(int f, int l, int r, int p, int v)//我们只需要更新它的lv, rv就可以了
{
if(l==r)
{
t[f].lv = t[f].rv = v;
return;
}
int m=(l+r)>>1;
if(p<=m) Update(L(f), l, m, p, v);//
else Update(R(f), m+1, r, p, v);//我们的目的是更新叶子结点,所以不需要更新区间
//当修改完成的时候我们要进行统计运算,也就是向上计算
Up(t[f], t[L(f)], t[R(f)]);
}
node Query(int f, int l, int r, int &L, int &R)
{
if(L<=l && r<=R) return t[f];
int m=(l+r)>>1;
node t1, t2;
t1.len=t2.len=0;
if(L<=m) t1=Query(L(f), l, m, L, R);//往左可走,就走
if(R>m) t2=Query(R(f), m+1, r, L, R);//往右可走,就走
if(t1.len && t2.len)//都找到了,从两边找到了
{
node tmp;
Up(tmp, t1, t2);
tmp.len=t1.len+t2.len;//哦哦,有可能多次更新,所以不能大意哦!!!
return tmp;
}
if(t1.len) return t1;
return t2;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n,m,x,y;
node te;
char c;
scanf("%d%d",&n,&m);
n--;
Build(1, 0,n);
getchar();
while(m--)
{
getchar();
scanf("%c%d%d",&c,&x,&y);
if(c=='Q')
{
te=Query(1,0,n,x,y);
printf("%d\n",te.m);
}
else Update(1, 0, n, x, y);
}
}
return 0;
}
7.hdu 4046http://acm.hdu.edu.cn/showproblem.php?pid=4046 Panda
本题就是一道简单的线段树题目。
但是难就难在只有当我们经过一定的转化之后,才可以用线段树来做,转化的这个过程是比较困难的。
这就是本题的难点所在。转化 转化 转化 转化 再转化
View Code
#include "iostream"
#include "string"
#include "cstdio"
#include "cstring"
#include "algorithm"
using namespace std;
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define maxn 50010
typedef struct node
{
int l, r, s;
}node;
node t[maxn<<2];
char str[maxn];
int a[maxn];
void build(int f, int l, int r)
{
t[f].l = l;
t[f].r = r;
if(l==r)
{
t[f].s=a[l];
return;
}
int m=(l+r)>>1;
build(L(f), l, m);
build(R(f), m+1, r);
t[f].s=t[L(f)].s+t[R(f)].s;
}
void update(int f, int l, int r, int p, int v)
{
if(l==r)
{
t[f].s=v;
return;
}
int m=(l+r)>>1;
if(p<=m) update(L(f), l, m, p, v);
else update(R(f), m+1, r, p, v);
t[f].s=t[L(f)].s+t[R(f)].s;
}
int Query(int f, int l, int r, int L, int R)
{
if(L<=l && r<=R) return t[f].s;
int m=(l+r)>>1;
int ret=0;
if(L<=m) ret += Query(L(f), l, m, L, R);
if(R>m) ret += Query(R(f), m+1, r, L, R);
return ret;
}
int main()
{
int cas,T=0,n,m,r,l,c;
char st;
scanf("%d",&cas);
while(cas--)
{
scanf("%d %d",&n,&m);
scanf("%s",str);
memset(a,0,sizeof(a));
printf("Case %d:\n",++T);
for(int i=2;i<n;i++)//标记以该下标结尾的长度为3的子串是否符合要求
{
if(str[i]!='w')
a[i]=0;
else if(str[i-1]=='b'&&str[i-2]=='w')
a[i]=1;
}
build(1,0,n);
while(m--)
{
scanf("%d",&c);
if(c==0)
{
scanf("%d %d",&l,&r);
l+=2;
if(l>r) printf("0\n");
else printf("%d\n",Query(1,0,n,l,r));
}
else
{
scanf("%d %c",&l,&st);
if(str[l]==st)//若要就该的字符跟原先相同,则不用修改了
continue;
str[l]=st;
if(l>=2&&str[l-2]=='w'&&str[l-1]=='b'&&str[l]=='w')
{
if(a[l]==0)//若改之前是不符合要求的串,则更新
update(1, 0,n,l,1);
a[l]=1;
}
else if(l>=2&&a[l]==1)//若改之前是符合要求的串,则更新
{
update(1, 0,n,l,0);
a[l]=0;
}
if(l>=1&&str[l]=='b'&&str[l-1]=='w'&&str[l+1]=='w')
{
if(a[l+1]==0)
update(1, 0,n,l+1,1);
a[l+1]=1;
}
else if(l<n-1&&l>=1&&a[l+1]==1) a[l+1]=0,update(1, 0,n,l+1,0);
if(str[l]=='w'&&str[l+1]=='b'&&str[l+2]=='w')
{
if(a[l+2]==0)
update(1, 0,n,l+2,1);
a[l+2]=1;
}
else if(l<n-2&&a[l+2]==1) a[l+2]=0,update(1, 0,n,l+2,0);
}
}
}
return 0;
}
来源:https://www.cnblogs.com/o8le/archive/2012/08/21/2649217.html
