@sensitive-cs
2017-03-30T20:33:10.000000Z
字数 8596
阅读 905
题解
HDU 1166:敌兵布阵
题意:
给出n个数,有3种操作,一种是对某个数加上一个数,一种是对某个数减去一个数,一种是查询一个区间内的数的和。
思路:
线段树裸题,主要是点更新,也可以用树状数组做。(巨坑的是位运算的时候以为除以2,就写的2,导致wa了几发,引以为戒。
代码:
#include <stdio.h>
#include <string.h>
int a[50005];
int sum[200005];
void pushup(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt << 1|1];
}
void build(int l,int r,int rt)
{
if (l == r)
{
sum[rt] = a[l];
return;
}
int m = (l+r) >> 1;
build(l,m,rt << 1);
build(m+1,r,rt << 1|1);
pushup(rt);
}
void update(int ll,int c,int l,int r,int rt)
{
if (l == r)
{
sum[rt] += c;
return;
}
int m = (l+r) >> 1;
if (ll <= m) update(ll,c,l,m,rt << 1);
else update(ll,c,m+1,r,rt << 1|1);
pushup(rt);
}
int query(int ll,int rr,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
return sum[rt];
}
int m = (l+r) >> 1;
int ans = 0;
if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
return ans;
}
int main()
{
int t;
scanf("%d",&t);
for (int o = 1;o <= t;o++)
{
int n;
scanf("%d",&n);
for (int i = 1;i <= n;i++)
scanf("%d",&a[i]);
build(1,n,1);
char ch[20];
printf("Case %d:\n",o);
while (scanf("%s",ch) != EOF)
{
if (ch[0] == 'E') break;
int x,y;
scanf("%d%d",&x,&y);
if (ch[0] == 'S')
{
update(x,-y,1,n,1);
}
if (ch[0] == 'A')
{
update(x,y,1,n,1);
}
if (ch[0] == 'Q')
{
int ans = 0;
ans = query(x,y,1,n,1);
printf("%d\n",ans);
}
}
}
return 0;
}
poj 3468:A SIMPLE PROBLEM WITH INTEGERS
题意:
给出n个数字,两种操作,一种是给给定区间内的每一个数加上一个数,另一种是查询某个区间内的数字之和。
思路:
纯线段树裸题,直接套模板。不过要注意先建树(又差点卡死2333,再进行操作。
代码:
#include <string.h>
#include <stdio.h>
int a[100005];
long long add[400005];
long long sum[400005];
void pushup(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1|1];
}
void build(int l,int r,int rt)
{
if (l == r)
{
sum[rt] = a[l];
return;
}
int m = (l+r) >> 1;
build(l,m,rt << 1);
build(m+1,r,rt << 1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn)
{
if (add[rt])
{
add[rt << 1] += add[rt];
add[rt << 1|1] += add[rt];
sum[rt << 1] += ln * add[rt];
sum[rt << 1|1] += rn *add[rt];
add[rt] = 0;
}
}
void update(int ll,int rr,int c,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
sum[rt] += c*(r-l+1);
add[rt] += c;
return;
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
if (ll <= m) update(ll,rr,c,l,m,rt << 1);
if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);
pushup(rt);
}
long long query(int ll,int rr,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
return sum[rt];
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
long long ans = 0;
if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
return ans;
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
memset(a,0,sizeof(a));
memset(add,0,sizeof(add));
memset(sum,0,sizeof(sum));
for (int i = 1;i <= n;i++)
scanf("%d",&a[i]);
build(1,n,1);
for (int i = 1;i <= q;i++)
{
char s[10];
scanf("%s",s);
int x,y,z;
if (s[0] == 'Q')
{
scanf("%d%d",&x,&y);
long long res = query(x,y,1,n,1);
printf("%I64d\n",res);
}
else
{
scanf("%d%d%d",&x,&y,&z);
update(x,y,z,1,n,1);
}
}
return 0;
}
HDU 1556:COLOR THE BALL
题意:
给出n个区间,每次对1个区间进行更新,对区间的每个数都加1,问每个数被更新了多少次。
思路:
线段树裸题,区间更新,单点查询,也可以用区间查询,左右端点相同就可以了。
代码:
#include <string.h>
#include <stdio.h>
int a[100005];
int add[400005];
int sum[400005];
void pushup(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1|1];
}
void build(int l,int r,int rt)
{
if (l == r)
{
sum[rt] = a[l];
return;
}
int m = (l+r) >> 1;
build(l,m,rt << 1);
build(m+1,r,rt << 1|1);
pushup(rt);
}
void pushdown(int rt,int ln,int rn)
{
if (add[rt])
{
add[rt << 1] += add[rt];
add[rt << 1|1] += add[rt];
sum[rt << 1] += ln * add[rt];
sum[rt << 1|1] += rn *add[rt];
add[rt] = 0;
}
}
void update(int ll,int rr,int c,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
sum[rt] += c*(r-l+1);
add[rt] += c;
return;
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
if (ll <= m) update(ll,rr,c,l,m,rt << 1);
if (rr > m) update(ll,rr,c,m+1,r,rt << 1|1);
pushup(rt);
}
long long query(int ll,int rr,int l,int r,int rt)
{
if (ll <= l && r <= rr)
{
return sum[rt];
}
int m = (l+r) >> 1;
pushdown(rt,m-l+1,r-m);
long long ans = 0;
if (ll <= m) ans += query(ll,rr,l,m,rt << 1);
if (rr > m) ans += query(ll,rr,m+1,r,rt << 1|1);
return ans;
}
int main()
{
int n;
while (scanf("%d",&n) == 1&& n)
{
memset(a,0,sizeof(a));
memset(add,0,sizeof(add));
memset(sum,0,sizeof(sum));
build(1,n,1);
for (int i = 1;i <= n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
update(a,b,1,1,n,1);
}
for (int i = 1;i <= n;i++)
{
int res = query(i,i,1,n,1);
if (i == 1) printf("%d",res);
else printf(" %d",res);
}
printf("\n");
}
return 0;
}
HDU1540:tunnel warfare
题意:
有n个村庄,他们是连着的,由于战争,有些村庄被毁了,被毁了之后又恢复。现在有3种指令,一种是毁灭村庄,另一种是回复村庄,还有一种是查询一个村庄连着的村庄有多少个。
思路1:
由于是跟区间有关,求区间的最大长度,所以说用线段树的区间合并,注释在代码中。
思路2:
看了一篇博客,博主天纵奇才,想到只用线段树的单点更新。
比如现在有1 2 3 4 5 6 7 8有8个村庄,我回去2和7,查询4的时候就是7-2-1。
博主想到最大值树和最小值树,最大值树就用一个很大的之来初始化,最小值数就用-1初始化,每次毁掉村庄,就用这个村庄的端点去覆盖-1或者最大值。当查询的时候,就用查询点左边的最大值,和查询点右边的最小值,进行如7-2-1这样的操作就行了。
注意防止越界的方法,把0点赋值为-1,n+1点赋值为无穷大就行了。
代码1:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int cnt = 50010;
struct ss
{
int l,r;
int ls,rs,ms;
}node[4*cnt];
int st[cnt];
void build(int l,int r,int rt)
{
node[rt].l = l,node[rt].r = r;
node[rt].ls = node[rt].rs = node[rt].ms = r - l + 1;
if (l != r)
{
int mid = (l+r) >> 1;
build(l,mid,rt << 1);
build(mid+1,r,rt << 1|1);
}
return;
}
void update(int rt,int x,int flag)
{
if (node[rt].l == node[rt].r)
{
node[rt].ls = node[rt].ms = node[rt].rs = flag;
return;
}
int mid = (node[rt].l + node[rt].r) >> 1;
if (x <= mid) update(rt << 1,x,flag);
else update(rt << 1|1,x,flag);
node[rt].ls = node[rt << 1].ls;
node[rt].rs = node[rt << 1|1].rs;
if (node[rt << 1].ms == node[rt << 1].r - node[rt << 1].l + 1)
node[rt].ls += node[rt << 1|1].ls;
if (node[rt << 1|1].ms == node[rt << 1|1].r - node[rt << 1|1].l+1)
node[rt].rs += node[rt << 1].rs;
node[rt].ms = max(max(node[rt<<1].ms,node[rt << 1|1].ms),node[rt << 1].rs + node[rt << 1|1].ls);
}
int query(int rt,int x)
{
if (node[rt].r == node[rt].l || node[rt].ms == 0 || node[rt].ms == node[rt].r - node[rt].l + 1)
{
return node[rt].ms;
}
int mid = (node[rt].l + node[rt].r) >> 1;
if (x <= mid)
{
if (x < node[rt << 1].r - node[rt << 1].rs + 1)
return query(rt << 1,x);
else
return node[rt << 1].rs + node[rt << 1|1].ls;
}
else
{
if (x > node[rt << 1|1].l + node[rt << 1|1].ls - 1)
return query(rt << 1|1,x);
else
return node[rt << 1].rs + node[rt << 1|1].ls;
}
}
int main()
{
int n,m;
while (scanf("%d%d",&n,&m) != EOF)
{
memset(st,0,sizeof(st));
build(1,n,1);
int top = 0;
for (int i = 0;i < m;i++)
{
char t[5];
scanf("%s",t);
if (t[0] == 'D')
{
int x;
scanf("%d",&x);
update(1,x,0);
st[top++] = x;
}
if (t[0] == 'R')
{
int x = st[--top];
update(1,x,1);
}
if (t[0] == 'Q')
{
int x;
scanf("%d",&x);
int sum = query(1,x);
printf("%d\n",sum);
}
}
}
return 0;
}
代码2
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int cnt = 5e4+10;
int ma[cnt << 2];
int mi[cnt << 2];
int st[cnt];
int g = inf,v = -1;
int m,n;
void update(int rt,int x,int flag,int l,int r)
{
if (l == r)
{
if (flag)
{
ma[rt] = -1;
mi[rt] = inf;
}
else
mi[rt] = ma[rt] = x;
return;
}
int mid = (l+r) >> 1;
if (x <= mid) update(rt << 1,x,flag,l,mid);
else update(rt << 1|1,x,flag,mid+1,r);
ma[rt] = max(ma[rt << 1],ma[rt << 1|1]);
mi[rt] = min(mi[rt << 1],mi[rt << 1|1]);
return;
}
void query1(int rt,int l,int r,int L,int R)
{
if (l >= L && r <= R)
{
g = min(g,mi[rt]);
return;
}
int mid = (l+r) >> 1;
if (L <= mid) query1(rt << 1,l,mid,L,R);
if (R > mid) query1(rt << 1|1,mid+1,r,L,R);
}
void query2(int rt,int l,int r,int L,int R)
{
if (l >= L && r <= R)
{
v = max(v,ma[rt]);
return;
}
int mid = (l+r) >> 1;
if (L <= mid) query2(rt << 1,l,mid,L,R);
if (R > mid) query2(rt << 1|1,mid+1,r,L,R);
}
int main()
{
while (scanf("%d%d",&n,&m) != EOF)
{
memset(ma,-1,sizeof(ma));
memset(mi,inf,sizeof(mi));
memset(st,0,sizeof(st));
int top = 0;
update(1,0,0,0,n+1);
update(1,n+1,0,0,n+1);
for (int i = 0;i < m;i++)
{
char s[5];
scanf("%s",s);
if (s[0] == 'D')
{
int x;
scanf("%d",&x);
st[top++] = x;
update(1,x,0,0,n+1);
}
if (s[0] == 'R')
{
int x = st[--top];
update(1,x,1,0,n+1);
}
if (s[0] == 'Q')
{
int x;
scanf("%d",&x);
g = inf;
v = -1;
query1(1,0,n+1,x,n+1);
query2(1,0,n+1,0,x);
if (g == v)
printf("0\n");
else
printf("%d\n",g - v - 1);
}
}
}
return 0;
}
poj2528:mayor's posters
题意:
相当于区间染色问题,有一面墙,开始时没有颜色,给出n个操作,每一次染一种颜色,每次染的颜色都不同,问最后墙上有多少颜色。
思路:
一开始想到了是线段树,但是奈何区间太大,1000万的数量级。后来搜题解报告知道了是离散化,所以就浅浅的学习了一下。离散化,对于这题,就是把大的区间映射到小的区间以减小复杂度。但是这题的离散化比较特殊,需要在不连续的坐标之间随意添加一个中间小的数,才能够保证不出错,这一点可以自己举例子理解。最开始写线段树,忘了,就偷懒写了简化版的线段树,果断tle,后来再次按照模板写,wa了,再一看,居然是pushdown在query的时候也需要下方,唉,还是不要懒,more haste,less speed。谨记!!!
代码:
#include <stdio.h>
#include <string.h>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
bool cx[10000005];
vector<int> xl;
int x[10005],y[10005];
int a[10000005];
int ans[10005];
int b[200005],lazy[200005];
void pushdown(int rt)
{
if (lazy[rt] != -1)
{
lazy[rt << 1] = lazy[rt << 1|1] = lazy[rt];
b[rt << 1] = b[rt << 1|1] = b[rt] = lazy[rt];
lazy[rt] = -1;
}
}
void update(int l,int r,int k,int ll,int rr,int rt)
{
//printf("***\n");
if (l >= ll && r <= rr)
{
b[rt] = k;
lazy[rt] = k;
return;
}
else
{
pushdown(rt);
int mid = (l + r) >> 1;
if (ll <= mid) update(l,mid,k,ll,rr,rt << 1);
if (rr > mid) update(mid+1,r,k,ll,rr,rt << 1|1);
}
}
void query(int l,int r,int rt)
{
if (l == r)
{
ans[b[rt]]++;
return;
}
pushdown(rt);
int mid = (l+r) >> 1;
query(l,mid,rt << 1);
query(mid+1,r,rt << 1|1);
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int n;
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
memset(cx,0,sizeof(cx));
memset(b,0,sizeof(b));
memset(lazy,-1,sizeof(lazy));
scanf("%d",&n);
xl.clear();
for (int i = 1;i <= n;i++)
{
scanf("%d%d",&x[i],&y[i]);
if (!cx[x[i]])
{
cx[x[i]] = 1;
xl.push_back(x[i]);
}
if (!cx[y[i]])
{
cx[y[i]] = 1;
xl.push_back(y[i]);
}
}
sort(xl.begin(),xl.end());
int len = xl.size();
for (int i = 1;i < len;++i)
{
if (xl[i] - xl[i-1] > 1)
xl.push_back(xl[i] - 1);
}
sort(xl.begin(),xl.end());
len = xl.size();
for (int i = 0;i < len;i++)
{
a[xl[i]] = i+1;
}
for (int i = 1;i <= n;i++)
{
x[i] = a[x[i]];
y[i] = a[y[i]];
}
for (int i = 1;i <= n;i++)
{
update(1,len,i,x[i],y[i],1);
}
int sum = 0;
query(1,len,1);
for (int i = 1;i <= n;i++)
{
if (ans[i]) sum++;
}
printf("%d\n",sum);
}
return 0;
}