http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=604&pid=1002
Dylans loves sequence
Accepts: 249
Submissions: 806
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
Problem Description
Dylans is given N
numbers a[1]....a[N]
And there are Q
questions.
Each question is like this (L,R)
his goal is to find the “inversions” from number L
to number R
.
more formally,his needs to find the numbers of pair(x,y
), that L≤x,y≤R
and x<y
and a[x]>a[y]
Input
In the first line there is two numbers N
and Q
.
Then in the second line there are N
numbers:a[1]..a[N]
In the next Q
lines,there are two numbers L,R
in each line.
N≤1000,Q≤100000,L≤R,1≤a[i]≤2 31 −1
Output
For each query,print the numbers of "inversions”
Sample Input
3 2 3 2 1 1 2 1 3
Sample Output
1 3
Hint
You shouldn‘t print any space in each end of the line in the hack data.
动态规划写法:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <string> #include <vector> #include <cmath> using namespace std; int dp[1005][1005],dp2[1005][1005]; int main() { int n,Q; while(scanf("%d%d",&n,&Q)==2) { int a[1005]; for(int i=0;i<n;++i) scanf("%d",&a[i]); for(int i=0;i<n;++i) { dp[i][i]=0; for(int j=i+1;j<n;++j) { dp[i][j]=dp[i][j-1]; if(a[j]<a[i]) dp[i][j]++; } } for(int i=0;i<n;++i) { dp2[i][0]=dp[0][i]; for(int j=1;j<=i;++j) dp2[i][j]=dp2[i][j-1]+dp[j][i]; } while(Q--) { int l,r; scanf("%d%d",&l,&r); l--;r--; if(l==0) printf("%d\n",dp2[r][r]); else printf("%d\n",dp2[r][r]-dp2[r][l-1]); } } return 0; }
//树状数组写法#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int i,j,m,n,p,k,a[1005],q,b[1005],tree[1005],l,r,ans[1005][1005]; int lowbit(int x) { return x&-x; } int ask(int x) { int sum=0; for (;x;x-=lowbit(x)) sum+=tree[x]; return sum; } int ins(int x) { for (;x<=b[0];x+=lowbit(x)) tree[x]++; } int main() { scanf("%d%d",&n,&q); for (i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1); b[0]=unique(b+1,b+n+1)-(b+1); for (i=1;i<=n;++i) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b; for (i=1;i<=n;++i) { memset(tree,0,sizeof(tree)); for (j=i;j<=n;++j) { ans[i][j]=ans[i][j-1]+ask(b[0])-ask(a[j]); ins(a[j]); } } for (;q--;) { scanf("%d%d",&l,&r); printf("%d\n",ans[l][r]); } }
线段树:
《1》http://poj.org/problem?id=3264(简单题,练模板)
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; const int MAXN = 200005; const int INF = 1000000000; int nMax, nMin; struct Node{ int L, R; int nMin, nMax; }segTree[MAXN*3]; int a[MAXN]; void Build(int i, int L, int R) { segTree[i].L = L; segTree[i].R = R; if(L==R) { segTree[i].nMin = segTree[i].nMax = a[L]; return; } int mid = (L+R)>>1; Build(i<<1, L, mid); Build(i<<1|1, mid+1, R); segTree[i].nMin=min(segTree[i<<1].nMin, segTree[i<<1|1].nMin); segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax); } void Query(int i, int L, int R) { if(segTree[i].nMax<=nMax&&segTree[i].nMin>=nMin) return; if(segTree[i].L==L&&segTree[i].R==R) { nMax = max(segTree[i].nMax, nMax); nMin = min(segTree[i].nMin, nMin); return; } int mid = (segTree[i].L+segTree[i].R)>>1; if(R<=mid) Query(i<<1, L, R); else if(L>mid) Query(i<<1|1, L, R); else { Query(i<<1, L, mid); Query(i<<1|1, mid+1, R); } } int main() { int n, q, L, R, i; while(scanf("%d%d", &n, &q)!=EOF) { for(i=1; i<=n; i++) scanf("%d", &a[i]); Build(1, 1, n); for(i=1; i<=q; i++) { scanf("%d%d", &L, &R); nMax = -INF; nMin = INF; Query(1, L, R); printf("%d\n", nMax-nMin); } } return 0; }
《2》http://acm.hdu.edu.cn/showproblem.php?pid=1166(简单题, 用模板)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 50000+5; int num[MAXN]; struct Node{ int l, r; int nSum; }segTree[MAXN*3]; void Build(int i, int l, int r) { segTree[i].l = l; segTree[i].r = r; if(l==r) { segTree[i].nSum=num[l]; return; } int mid=(l+r)>>1; Build(i<<1, l, mid); Build(i<<1|1, mid+1, r); segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum; } void Add(int i, int t, int b) { segTree[i].nSum+=b; if(segTree[i].l==t&&segTree[i].r==t) return; int mid=(segTree[i].l+segTree[i].r)>>1; if(t<=mid) Add(i<<1, t, b); else Add(i<<1|1, t, b); } int Query(int i, int l, int r) { if(segTree[i].l==l&&segTree[i].r==r) return segTree[i].nSum; int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) return Query(i<<1, l, r); else if(l>mid) return Query(i<<1|1, l, r); else return Query(i<<1, l, mid)+Query(i<<1|1, mid+1, r); } int main() { int T; int kase; int n, i; char str[10]; int a, b; scanf("%d", &T); for(kase=1; kase<=T; kase++) { scanf("%d", &n); for(i=1; i<=n; i++) scanf("%d", &num[i]); Build(1, 1, n); printf("Case %d:\n", kase); while(scanf("%s", &str)) { if(str[0]==‘E‘) break; //if(strcmp(str,"End")==0) break; scanf("%d%d", &a, &b); if(str[0]==‘A‘) Add(1, a, b); //if(strcmp(str, "Add")==0) Add(1, a, b); else if(str[0]==‘S‘) Add(1, a, -b); //else if(strcmp(str, "Sub")==0) Add(1, a, -b); else printf("%d\n", Query(1, a, b)); } } return 0; }
《3》http://poj.org/problem?id=3468(进阶题)
#include<cstdio> #include<algorithm> #include<iostream> using namespace std; const int MAXN = 100000; int num[MAXN]; struct Node{ int l, r; long long nSum; long long Inc; }segTree[MAXN*3]; void Build(int i, int l, int r)//建立线段树 { segTree[i].l=l; segTree[i].r=r; segTree[i].Inc=0; if(l==r) { segTree[i].nSum=num[l]; return; } int mid=(l+r)>>1; Build(i<<1, l, mid); Build(i<<1|1, mid+1, r); segTree[i].nSum=segTree[i<<1].nSum+segTree[i<<1|1].nSum; } void Add(int i, int a, int b, long long c)//在结点i的区间(a,b)上增加c { if(segTree[i].l==a&&segTree[i].r==b)//这个过程和建树过程相似。 { segTree[i].Inc+=c; return; } segTree[i].nSum+=c*(b-a+1); int mid=(segTree[i].l+segTree[i].r)>>1; if(b<=mid) Add(i<<1, a, b, c); else if(a>mid) Add(i<<1|1, a, b, c); else { Add(i<<1, a, mid, c); Add(i<<1|1, mid+1, b, c); } } long long Query(int i, int a, int b)//查询a-b的总和 { if(segTree[i].l==a&&segTree[i].r==b) { return segTree[i].nSum+(b-a+1)*segTree[i].Inc; } segTree[i].nSum+=(segTree[i].r-segTree[i].l+1)*segTree[i].Inc; int mid=(segTree[i].l+segTree[i].r)>>1; Add(i<<1, segTree[i].l, mid, segTree[i].Inc); Add(i<<1|1, mid+1, segTree[i].r, segTree[i].Inc); segTree[i].Inc = 0; if(b<=mid) return Query(i<<1, a, b); else if(a>mid) return Query(i<<1|1, a, b); else return Query(i<<1, a, mid)+Query(i<<1|1, mid+1, b); } int main() { int n, q; int i; int a, b, c; char ch; while(scanf("%d%d", &n, &q)!=EOF) { for(i=1; i<=n; i++) scanf("%d", &num[i]); Build(1, 1, n); for(i=1; i<=q; i++) { cin>>ch; if(ch==‘C‘) { scanf("%d%d%d", &a, &b, &c); Add(1, a, b, c); } else { scanf("%d%d", &a, &b); printf("%I64d\n", Query(1, a, b)); } } } return 0; }
《4》http://acm.hdu.edu.cn/showproblem.php?pid=1754(简单题)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 200000+10; int num[MAXN]; struct Node { int l, r; int nMax; }segTree[MAXN*4]; void Build(int i, int l, int r) { segTree[i].l=l; segTree[i].r=r; if(l==r) { segTree[i].nMax=num[l]; return; } int mid=(segTree[i].l+segTree[i].r)>>1; Build(i<<1, l, mid); Build(i<<1|1, mid+1, r); segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax); } void Update(int i, int l, int r, int x) { if(segTree[i].l==l&&segTree[i].r==r) { segTree[i].nMax=x; return; } int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) Update(i<<1, l, r, x); else if(l>mid) Update(i<<1|1, l, r, x); else { Update(i<<1, l, mid, x); Update(i<<1|1, mid+1, r, x); } segTree[i].nMax=max(segTree[i<<1].nMax, segTree[i<<1|1].nMax); } int Query(int i, int l, int r) { if(segTree[i].l==l&&segTree[i].r==r) { return segTree[i].nMax; } int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) return Query(i<<1, l, r); else if(l>mid) return Query(i<<1|1, l, r); else return max(Query(i<<1, l, mid), Query(i<<1|1, mid+1, r)); } int main() { int n, m; while(scanf("%d%d", &n, &m)!=EOF) { int i, j, k, a, b; char s[2]; for(i=1; i<=n; i++) scanf("%d", &num[i]); Build(1, 1, n); for(i=0; i<m; i++) { scanf("%s%d%d", s, &a, &b); if(s[0]==‘U‘) Update(1, a, a, b); else printf("%d\n", Query(1, a, b)); } } return 0; }
《5》http://acm.hdu.edu.cn/showproblem.php?pid=2795
线段树, 适用的范围还真广!!。 这个题看到别人能用线段树切, 也是非常的机智。
/*以墙的高度和广告数量的较小值建线段树 , 树中v表示该段中最大空余量,idid表示最大 空余量所在行 (从上开始记)。每次贴广告 先比较空余量, 从上到下,空余足够就贴, 再根据查找到的空余行进行点更新。。。 */ #include<cstdio> #include<algorithm> #include<iostream> using namespace std; const int MAXN = 200000+10; int h, v, n; struct Node { int l, r; int id; int v; }segTree[MAXN*4]; void Build(int i, int l, int r) { segTree[i].l = l; segTree[i].r = r; segTree[i].id = l; segTree[i].v = v; if(l==r) return; int mid=(l+r)>>1; Build(i<<1, l, mid); Build(i<<1|1, mid+1, r); } int Query(int i, int val) { if(segTree[i].v<val) return -1; if(segTree[i].l==segTree[i].r) return segTree[i].id; if(segTree[i<<1].v>=val) return Query(i<<1, val); else return Query(i<<1|1, val); } void Update(int i, int l, int r, int val) { if(segTree[i].l==l&&segTree[i].r==r) { segTree[i].v-=val; return; } int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) Update(i<<1, l, r, val); else if(l>mid) Update(i<<1|1, l, r, val); if(segTree[i<<1].v>segTree[i<<1|1].v) { segTree[i].v = segTree[i<<1].v; segTree[i].id = segTree[i<<1].id; } else { segTree[i].v = segTree[i<<1|1].v; segTree[i].id = segTree[i<<1|1].id; } } int main() { while(scanf("%d%d%d", &h, &v, &n)!=EOF) { int j, k; if(h>n) h = n; Build(1, 1, h); for(int q=0; q<n; q++) { scanf("%d", &k); int t; t = Query(1, k); printf("%d\n",t); if(t!=-1) Update(1, t, t, k); } } return 0; }
《6》http://acm.hdu.edu.cn/showproblem.php?pid=1394(求逆序数)
线段树法:
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int MAXN=5000+10; int a[MAXN]; struct Node { int l,r; int sum; }segTree[MAXN*3]; void Build(int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; if(l==r) { segTree[i].sum=0; return; } int mid=(l+r)>>1; Build(i<<1,l,mid); Build((i<<1)|1,mid+1,r); segTree[i].sum=0; } void add(int i,int t,int val) { segTree[i].sum+=val; if(segTree[i].l==segTree[i].r) { return; } int mid=(segTree[i].l+segTree[i].r)>>1; if(t<=mid) add(i<<1,t,val); else add((i<<1)|1,t,val); } int sum(int i,int l,int r) { if(segTree[i].l==l&&segTree[i].r==r) return segTree[i].sum; int mid=(segTree[i].l+segTree[i].r)>>1; if(r<=mid) return sum(i<<1,l,r); else if(l>mid) return sum((i<<1)|1,l,r); else return sum(i<<1,l,mid)+sum((i<<1)|1,mid+1,r); } int main() { int n; while(scanf("%d",&n)!=EOF) { Build(1,0,n-1); for(int i=0;i<n;i++) scanf("%d",&a[i]); int ans=0; for(int i=0;i<n;i++) { ans+=sum(1,a[i],n-1); add(1,a[i],1); } int Min=ans; for(int i=0;i<n;i++) { ans-=a[i];//减少的逆序数 ans+=n-a[i]-1;//增加的逆序数 if(ans<Min)Min=ans; } printf("%d\n",Min); } return 0; }
线段树求逆序数, 其实就是将输入的数组元素依次插入线段树, 线段树的段值sum表示该段中以插入的数字个数。每次插入,计算已插入的比插入数大的数的个数,总和就是逆序数。
树状数组法:(先占个坑, 嘿嘿!)
《7》http://poj.org/problem?id=2528(线段树+离散)
这道题看似并不复杂,然而此坑甚深。 本来想暴力一下的, 结果,,,,, 其实大多数的能用线段树解决的问题,都有比较简单的暴力思路, 然而大都卡时间。这就体现出线段树的高效性啦! 。 这也是为何算法令人如痴如醉的一个原因-------------高效性。
暴力该打:
#include<iostream> #include<cstdio> #include<cstring> #include<set> using namespace std; const int MAXN = 10000000+10; int a[MAXN]; int main() { int T, n; int l, r; scanf("%d", &T); while(T--) { set<int> ss; memset(a, 0, sizeof(a)); scanf("%d", &n); for(int i=1; i<=n; i++) { scanf("%d%d", &l, &r); for(int j=l; j<=r; j++) a[j] = i; } for(int i=0; i<MAXN; i++) ss.insert(a[i]); printf("%d\n",ss.size()-1); } return 0; }
反对暴力, 提倡和谐!