题目来自大神博客的线段树专题
http://www.notonlysuccess.com/index.php/segment-tree-complete/
hdu1166 敌兵布阵
题意:O(-1)
思路:O(-1)
线段树功能:update:单点增减 query:区间求和
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <map> #include <utility> #include <queue> #include <stack> using namespace std; const int INF=1<<30; const double eps=1e-6; const int N = 50010; int sum[N<<2],n,M; void add(int x,int v) { for(x+=M;x;x>>=1) sum[x]+=v; } int query(int l,int r) { int res=0; for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1) { if(~l&1) res+=sum[l^1]; if(r&1) res+=sum[r^1]; } return res; } void run() { scanf("%d",&n); for(M=1;M<=n+1;M*=2); memset(sum,0,sizeof(sum)); int x,y; for(int i=1;i<=n;++i) { scanf("%d",&x); add(i,x); } char s[15]; static int cas=1; printf("Case %d:\n",cas++); while(scanf("%s",s)!=EOF && s[0]!='E') { scanf("%d%d",&x,&y); if(s[0]=='Q') printf("%d\n",query(x,y)); else if(s[0]=='A') add(x,y); else add(x,-y); } } int main() { // freopen("case.txt","r",stdin); int _; scanf("%d",&_); while(_--) run(); return 0; }
PS: 第一次交RE了一次,原因是,题目的n范围是5*10^5,我只开了数组比这个数的两倍大一点。但是zkw要求范围是2的k次幂那样的,所以开数组要开大点吧
hdu1754 I Hate It
题意:O(-1)
思路:O(-1)
线段树功能:update:单点替换 query:区间最值
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <map> #include <utility> #include <queue> #include <stack> using namespace std; const int INF=1<<30; const double eps=1e-6; const int N = 200000; int mes[N<<2],n,m,M; void update(int x,int v) { x+=M;mes[x]=v; for(x>>=1;x;x>>=1) mes[x]=max(mes[x<<1],mes[x<<1|1]); } int query(int l,int r) { int res=0; for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1) { if(~l&1) res=max(res,mes[l^1]); if(r&1) res=max(res,mes[r^1]); } return res; } void run() { for(M=1;M<=n+1;M<<=1); memset(mes,0,sizeof(mes)); int x,y; for(int i=1;i<=n;++i) { scanf("%d",&x); update(i,x); } char s[5]; while(m--) { scanf("%s%d%d",s,&x,&y); if(s[0]=='Q') printf("%d\n",query(x,y)); else update(x,y); } } int main() { // freopen("case.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF) run(); return 0; }
顺利1Y
hdu1394 Minimum Inversion Number
题意:求Inversion后的最小逆序数
思路:用O(nlogn)复杂度求出最初逆序数后,就可以用O(1)的复杂度分别递推出其他解。线段树是用在求最初逆序数上。递推方法详细在下面
线段树功能:update:单点增减 query:区间求和
递推: 因为题目总是把第一个数移到最后一个位置,所以原来比它小的数(和它构成逆序)在移动之后就不是逆序了,而原来比它大的数(不和它构成逆序)在移动之后就是逆序了,这样sum就变化了: Sum=sum-(low[a[i]])+(up[a[i]]); 显然在序列0,1,2,…..n-1中 比a[i]小的数的个数是 Low[a[i]]=a[i]; 比a[i]大的数的个数是 up[a[i]]=n-a[i]-1; 题目要求是循环移动n次,那么只要写个for,把a[0],a[1],a[2]……a[n-1]都移动一遍,sum进行n次上面的公式运算,同时记录最小值,就是最小逆序数了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <vector> #include <map> #include <utility> #include <queue> #include <stack> using namespace std; const int INF=1<<30; const double eps=1e-6; const int N = 5010; int sum[N<<2],n,M; int a[N]; inline void add(int x) { for(x+=M;x;x>>=1) ++sum[x]; } inline int query(int l,int r) { int res=0; for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1) { if(~l&1) res+=sum[l^1]; if(r&1) res+=sum[r^1]; } return res; } void run() { int s=0; memset(sum,0,sizeof(sum)); for(M=1;M<=n+1;M<<=1); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); ++a[i];//注意后面add的时候不能在0位置上add if(a[i]!=n) s+=query(a[i]+1,n); add(a[i]); } int ans=s; for(int i=1;i<=n;++i) { s = s + (n - a[i]) - (a[i] - 1); if(s<ans) ans=s; } printf("%d\n",ans); } int main() { // freopen("case.txt","r",stdin); while(scanf("%d",&n)!=EOF) run(); return 0; }
一个要注意的地方是zkw不能在0位置操作,所以应该把a[i]都加一
hdu2795 Billboard
题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子
思路:每次找到最大值的位子,然后减去L
线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)
跪了这个问题暂时没想到zkw怎么搞,这个是要根据当前区间最值来判断是去左子树还是右子树找,需要自顶向下的递归
//---------其他题目---------//
hdu4366
详细题解: hdu4366
需要用到线段树
单点更新,区间最值
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <stack> using namespace std; const int N = 50010; struct _edge{ int to,next; }; _edge edge[N*2]; int ecnt,head[N]; void addedge(int u,int v) { edge[ecnt].to = v; edge[ecnt].next = head[u]; head[u] = ecnt++; } struct node{ int id,a,b,l,r; friend bool operator < (const node &a, const node &b) { return a.a>b.a; } }; int n,m,M; int zkw[N*10][2]; node man[N]; int ans[N]; int dfscnt; void dfs(int u,int fa) { man[u].l = dfscnt++; for(int e=head[u];e!=-1;e=edge[e].next) { int &v = edge[e].to; if(v==fa) continue; dfs(v,u); } man[u].r = dfscnt++; } void add(int x,int a,int b) { for(x+=M;x;x>>=1) if(zkw[x][0]<a) zkw[x][0]=a,zkw[x][1]=b; } int query(int l,int r) { int a,b; a=b=-1; for(l=l+M-1,r=r+M+1;l^r^1;l>>=1,r>>=1) { if(~l&1 && zkw[l^1][0]>a) a=zkw[l^1][0],b=zkw[l^1][1]; if(r&1 && zkw[r^1][0]>a) a=zkw[r^1][0],b=zkw[r^1][1]; } return b; } void run() { scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); ecnt=0; int a,b,c; for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,i); addedge(i,a); man[i].a=c; man[i].b=b; man[i].id=i; } dfscnt=1; dfs(0,-1); // for(int i=0;i<n;i++) // printf("%d %d %d\n",i,man[i].l,man[i].r); sort(man+1,man+n); for(M=1;M<=dfscnt+1;M*=2); memset(zkw,-1,sizeof(zkw)); stack<int> stk; stk.push(1); ans[man[1].id]=-1; for(int i=2;i<n;i++) { if(man[i].a!=man[i-1].a) { while(!stk.empty()) { int u = stk.top(); stk.pop(); add(man[u].l,man[u].b,man[u].id); add(man[u].r,man[u].b,man[u].id); } } stk.push(i); ans[man[i].id] = query(man[i].l,man[i].r); } while(m--) { scanf("%d",&a); printf("%d\n",ans[a]); } } int main() { freopen("case.txt","r",stdin); int _; scanf("%d",&_); while(_--) run(); return 0; }