线段树模板,以POJ 3468为例
#include<cstdio> #define maxn 100005 typedef long long ll; struct node { int l,r; ll f,w; }tree[maxn<<2]; ll a[maxn],ans; void build(int l,int r,int k) { tree[k].l=l;tree[k].r=r; if (l==r) { tree[k].w=a[l]; return; } int mid=(l+r)>>1; build(l,mid,k<<1); build(mid+1,r,k<<1|1); tree[k].w=tree[k<<1].w+tree[k<<1|1].w; } void pushdown(int k) { tree[k<<1].f+=tree[k].f; tree[k<<1|1].f+=tree[k].f; tree[k<<1].w+=tree[k].f*(tree[k<<1].r-tree[k<<1].l+1); tree[k<<1|1].w+=tree[k].f*(tree[k<<1|1].r-tree[k<<1|1].l+1); tree[k].f=0; } void add(int l,int r,int k,ll x) { if (l<=tree[k].l && tree[k].r<=r) { tree[k].w+=(tree[k].r-tree[k].l+1)*x; tree[k].f+=x; return; } if (tree[k].f) pushdown(k); int mid=(tree[k].l+tree[k].r)>>1; if (l<=mid) add(l,r,k<<1,x); if (r>mid) add(l,r,k<<1|1,x); tree[k].w=tree[k<<1].w+tree[k<<1|1].w; } ll sum(int l,int r,int k) { if (l<=tree[k].l && tree[k].r<=r) { return tree[k].w; } if (tree[k].f) pushdown(k); int mid=(tree[k].l+tree[k].r)>>1; ll re=0; if (l<=mid) re+=sum(l,r,k<<1); if (r>mid) re+=sum(l,r,k<<1|1); return re; } int main() { int i,n,q,x,y; ll z; char op[5]; scanf("%d%d",&n,&q); for (i=1;i<=n;i++) scanf("%lld",&a[i]); build(1,n,1); while (q--) { scanf("%s",op); if (op[0]=='Q') { scanf("%d%d",&x,&y); printf("%lld ",sum(x,y,1)); } else { scanf("%d%d%lld",&x,&y,&z); add(x,y,1,z); } }//= = return 0; }
B POJ 2155
题目为区间修改单点查询,应想到树状数组。所以使用二维树状数组。
#include<cstdio> #include<cstring> #define maxn 1005 using namespace std; int c[maxn][maxn],n; int lowbit(int x){return x&(-x);} void add(int x,int y,int v) { int i,j; for (i=x;i<=n;i+=lowbit(i)) for (j=y;j<=n;j+=lowbit(j)) c[i][j]+=v; } int getsum(int x,int y) { int i,j,re=0; for (i=x;i>=1;i-=lowbit(i)) for (j=y;j>=1;j-=lowbit(j)) re+=c[i][j]; return re; } int main() { int i,t,q; int sx,sy,tx,ty; char op[20]; scanf("%d",&t); while (t--) { memset(c,0,sizeof(c)); scanf("%d%d",&n,&q); while (q--) { scanf("%s",op); if (op[0]=='C') { scanf("%d%d%d%d",&sx,&sy,&tx,&ty); add(sx,sy,1); add(tx+1,sy,-1); add(sx,ty+1,-1); add(tx+1,ty+1,1); } else { scanf("%d%d",&sx,&sy); printf("%d ",getsum(sx,sy)%2); } } printf(" "); } return 0; }
D HDU 3450
题意:给一个长度为n的序列,定义完美子序列满足长度不小于2且相邻的两个元素之差的绝对值不大于d,问该序列有多少完美子序列。
思路:首先可以想到一个DP做法,设dp[i]表示以第i位结尾的完美子序列数,
则有dp[i]=∑(dp[j]+1),j<i且|a[i]-a[j]|<=d,ans=∑dp[i]
DP做法复杂度为n2,不可行(n<=1e5)
考虑使用树状数组。先复制一个数组b,将其排序。这样就可以二分查找出|a[i]-a[j]|<=d在b数组中的最左端l和最右端r,
可用树状数组维护dp[i]+1,于是∑(dp[j]+1)=getsum(r)-getsum(l-1)。
(注意,因为要求字串长度不小于2,初始的所有位dp[i]+1=0)
更新当前的dp[i]+1=∑(dp[j]+1)+1,ans=ans+dp[i]=ans+∑(dp[j]+1)。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const int mod=9901; const int maxn=100005; int a[maxn],b[maxn],c[maxn],n; int lowbit(int x){return x&(-x);} void add(int x,int v) { int i; for (i=x;i<=n;i+=lowbit(i)) c[i]=(c[i]+v)%mod; } int getsum(int x) { int i,re=0; for (i=x;i;i-=lowbit(i)) re=(re+c[i])%mod; return re; } int main() { int d,i,ans; while (scanf("%d%d",&n,&d)!=EOF) { ans=0; memset(c,0,sizeof(c)); for (i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+n+1); for (i=1;i<=n;i++) { int l=lower_bound(b+1,b+n+1,a[i]-d)-b; int r=lower_bound(b+1,b+n+1,a[i]+d+1)-b; int t=lower_bound(b+1,b+n+1,a[i])-b; int x=(getsum(r-1)-getsum(l-1)+mod)%mod; add(t,x+1); ans=(ans+x)%mod; } printf("%d ",ans); } return 0; }
F HDU 3333
题意:给一个长度为n的序列,进行q次询问,每次询问区间[l,r]中不同大小数字的和。
思路:离线+树状数组+map。将q次询问按照r排序。
对于每一个数,利用map来记录前一个大小相同的数的位置。
每次将r前面未加入树状数组的数加入树状数组,从左至右依次加入。
如果发现之前有大小相同的数已经加入,就将其删去。
这样就能保证树状数组中每个大小不同的数只出现一次且出现的位置尽量靠右。
#include<cstdio> #include<cstring> #include<algorithm> #include<map> using namespace std; typedef long long ll; const int maxn=30005; const int maxq=100005; map<int,int>mp; int n,a[maxn],pre[maxn]; ll c[maxn],ans[maxq]; struct qu { int l,r,id; bool operator < (const qu &x)const { if (r==x.r) return l<x.l; return r<x.r; } }q[maxq]; int lowbit(int x){return x&(-x);} void add(int x,int v) { for (int i=x;i<=n;i+=lowbit(i)) c[i]+=v; } ll getsum(int x) { ll re=0; for (int i=x;i;i-=lowbit(i)) re+=c[i]; return re; } int main() { int i,j,T,Q; scanf("%d",&T); while (T--) { memset(c,0,sizeof(c)); mp.clear(); scanf("%d",&n); for (i=1;i<=n;i++) { scanf("%d",&a[i]); pre[i]=mp[a[i]]; mp[a[i]]=i; } scanf("%d",&Q); for (i=1;i<=Q;i++) { scanf("%d%d",&q[i].l,&q[i].r); q[i].id=i; } sort(q+1,q+Q+1); j=1; for (i=1;i<=Q;i++) { for (;j<=n && j<=q[i].r;j++) { if (pre[j]) add(pre[j],-a[j]); add(j,a[j]); } ans[q[i].id]=getsum(q[i].r)-getsum(q[i].l-1); } for (i=1;i<=Q;i++) printf("%lld ",ans[i]); } return 0; }
G HDU 4578
H HDU 2795
把每一行看作一个节点,行的长度看作节点的值,就能得到一个长度为h的序列。用线段树维护这个序列的区间最大值
每次操作时选取比x大的最小的行,并且更新行的长度
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxn 200005 using namespace std; typedef long long ll; struct node { int l,r,w; }tree[maxn<<2]; void build(int l,int r,int k,int w) { tree[k].l=l;tree[k].r=r; tree[k].w=w; if (l==r) return; int mid=(l+r)>>1; build(l,mid,k<<1,w); build(mid+1,r,k<<1|1,w); tree[k].w=max(tree[k<<1].w,tree[k<<1|1].w); } void update(int x,int k) { if (tree[k].l==tree[k].r) { printf("%d ",tree[k].l); tree[k].w-=x; return; } if (x<=tree[k<<1].w) update(x,k<<1); else update(x,k<<1|1); tree[k].w=max(tree[k<<1].w,tree[k<<1|1].w); } int main() { int h,w,n,i,x; while (scanf("%d%d%d",&h,&w,&n)!=EOF) { h=min(h,n); build(1,h,1,w); for (i=1;i<=n;i++) { scanf("%d",&x); if (tree[1].w>=x) update(x,1); else printf("-1 "); } } return 0; }
I HDU 6054
J HDU 6315