5.3.1 HDU1166 敵兵布陣
裸的樹狀數組
query[i..j]=sum(j)-sum(i-1)
#include <cstdio>#include <string.h> using namespace std;const int MAXN=50010;int n,cas,x,y; char op[6];//樹狀數組操作 int c[MAXN*2];void init(){memset(c,0,sizeof c);}int lowbit(int x){return x&-x;} void modify(int i,int k){while(i<n*2)c[i]+=k,i+=lowbit(i);}int sum(int i){int s=0;while(i>0)s+=c[i],i-=lowbit(i);return s;}//============int main(){scanf("%d",&cas);for(int ca=1;ca<=cas;ca++){init();scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&x);modify(i,x);}printf("Case %d:\n",ca);while(scanf("%s",op),strcmp(op,"End")){scanf("%d%d",&x,&y);if(strcmp(op,"Add")==0)modify(x,y);else if(strcmp(op,"Sub")==0)modify(x,-y);else printf("%d\n",sum(y)-sum(x-1));}}}
5.3.2 HDU2492 Ping Pong
在數組中統計小中大數以及大中小數
將數組中的數從左至右到右掃描,記錄右邊比它大的數以及比它小的數..
再從右至左掃描,記錄左邊比它大的數以及比它小的數
每個裁判舉辦的比賽=左小*右大+左大*右小,加起來即可
上面的操作可以用樹狀數組實現
#include <cstdio>#include <cstdlib>#include <string.h>using namespace std;const int MAXN=20010,MAXA=110010;int cas,n,ai[MAXN];int lmin[MAXN],rmin[MAXN],lmax[MAXN],rmax[MAXN];//每個裁判左右邊大/小於他的數的個數 //樹狀數組int c[MAXA*2];void init(){memset(c,0,sizeof c);}int lowbit(int x){return x&-x;}void modify(int x){while(x<MAXA*2)c[x]++,x+=lowbit(x);}int sum(int x){int s=0;while(x>0)s+=c[x],x-=lowbit(x);return s;}int main(){scanf("%d",&cas);while(cas--){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&ai[i]);//從左邊開始掃描,掃描左邊小於(大於)他的init();for(int i=1;i<=n;i++){lmin[i]=sum(ai[i]-1);lmax[i]=i-lmin[i]-1;modify(ai[i]);}//從右邊開始掃描,掃描右邊小於(大於)他的init();for(int i=n;i>=1;i--){rmin[i]=sum(ai[i]-1);rmax[i]=n-i-rmin[i];modify(ai[i]);}__int64 res=0;for(int i=1;i<=n;i++){res+=lmin[i]*rmax[i]+lmax[i]*rmin[i];} printf("%I64d\n",res);} return 0;}
5.3.3
HDU3584 Cube
三維的樹狀數組
#include <cstdio>#include <string.h>using namespace std;const int MAXN=105;/*三維樹狀數組記錄(0,0,0)->(x,y,z)這個點的修改次數使用容斥原理得到(x1,y1,z1)->(x2,y2,z2); 在(x1,y1,z1)處+1,(x2,y2,z2)處-1 %2就能得到結果 */int c[MAXN][MAXN][MAXN];int lowbit(int x){return x&-x;} int modify(int x, int y, int z, int num){int j, k;while(x < MAXN){j = y;while(j < MAXN){k = z;while(k < MAXN){c[x][j][k] += num;k += lowbit(k);}j += lowbit(j);}x += lowbit(x);}} int sum(int x,int y,int z){int s = 0;int j,k;while(x > 0){j = y;while(j > 0){k = z;while(k > 0){s += c[x][j][k];k -= lowbit(k);}j -= lowbit(j);}x -= lowbit(x);} return s;} int main(){int n,m,op,x1,y1,z1,x2,y2,z2;while(scanf("%d%d", &n, &m) != EOF){memset(c, 0, sizeof c);while(m--){scanf("%d%d%d%d", &op, &x1, &y1, &z1);if(op==1){scanf("%d%d%d", &x2, &y2, &z2);modify(x1, y1, z1, 1); modify(x2+1, y1, z1, -1); modify(x1, y2+1, z1, -1); modify(x1, y1, z2+1, -1); modify(x1, y2+1, z2+1, 1); modify(x2+1, y1, z2+1, 1); modify(x2+1, y2+1, z1, 1); modify(x2+1, y2+1, z2+1, -1);}else{printf((sum(x1, y1, z1) & 1)?"1\n":"0\n");} }}return 0;}
5.3.4 HDU2586 How far away ?
5.3.5 HDU2874 Connections between cities
兩題都是LCA問題,用LCA_Targin演算法就可以解決,兩題的公式都是dis(u,v)=dis(u,root)+dis(v,root)-2*dis(lca(u,v),root)
其中lca(u,v)是u,v的最近公用祖先
2586比較裸,直接套演算法就可以了
2874首先要判斷兩個點是否連通,這裡可以用並查集實現,只有在一個區塊中才去查詢.
然後虛擬一個根節點,根節點向每個區塊連一條線,對這個根節點進行LCA_Targin演算法即可
2874的記憶體比較緊張..注意節省空間的..這裡貼的是2874的代碼
#include <cstdio>#include <string.h>#include <vector>#include <cstdlib>using namespace std;int n,m,c,ans[1000001],hash[10001];//圖操作struct edge{int v,w;edge(int b,int c){v=b,w=c;}};struct edge2{int v,ind;edge2(int b,int c){v=b,ind=c;}};vector<edge> ed[10001];vector<edge2> qr[10001];//並查集int p[10010];int find(int x){return x==p[x]?x:p[x]=find(p[x]);}void merge(int x,int y){p[find(y)]=find(x);}//LCAbool vis[10001];int dis[10001];void LCA_Targin(int u){vis[u]=1;for(size_t i=0;i<qr[u].size();i++){int v=qr[u][i].v;if(vis[v])ans[qr[u][i].ind]=dis[u]+dis[v]-2*dis[find(v)];}for(size_t i=0;i<ed[u].size();i++){int v=ed[u][i].v;if(!vis[v]){dis[v]=dis[u]+ed[u][i].w;LCA_Targin(v);merge(u,v);}}}//初始化 void init(){memset(vis,0,sizeof vis);memset(hash,0,sizeof hash);for(int i=0;i<10001;i++){p[i]=i;ed[i].clear();qr[i].clear();}}int main(){while(scanf("%d%d%d",&n,&m,&c)!=EOF){init();int x,y,z;for(int i=0;i<m;i++){scanf("%d%d%d",&x,&y,&z);ed[x].push_back(edge(y,z));ed[y].push_back(edge(x,z));merge(x,y);}for(int i=0;i<c;i++){scanf("%d%d",&x,&y);if(find(x)!=find(y)){//對於不連通的點 ans[i]=-1;continue;}qr[x].push_back(edge2(y,i));qr[y].push_back(edge2(x,i));}//添加虛擬根節點,向每個塊增加一條連線 for(int i=1;i<=n;i++){int t=find(i);if(hash[t]==0){hash[t]=1;ed[0].push_back(edge(t,0));ed[t].push_back(edge(0,0));}}for(int i=0;i<10010;i++)p[i]=i;dis[0]=0;LCA_Targin(0);for(int i=0;i<c;i++){if(ans[i]==-1)printf("Not connected\n");else printf("%d\n",ans[i]);}}return 0;}
5.3.6 HDU3486 Interviewe
一開始寫了個線段樹..TLE了..後來想想其實這題線段樹還沒有樸素演算法快...因為題中將n分為k個區間,查詢這每個區間中的最大值,樸素演算法的複雜度一定是O(N),線段樹對於一次查詢是O(LogN),總共就是O(k*LogN),比較可以發現,大多數情況下,線段樹是不如樸素演算法快的...以後要具體題目具體對待..不能盲目的敲代碼..
#include <cstdio>using namespace std;const int MAXN=200001;int n,k,v[MAXN];int mmax(int a,int b){return a>b?a:b;}int r;int getmax(int mid){int rs=0,tk=n/mid;for(int i=1;i<=mid;i++){int maxn=-1;for(int j=tk*(i-1)+1;j<=tk*i;j++)maxn=mmax(v[j],maxn);rs+=maxn;if(rs>k){r=mid;return 1;}}return 0;}int main(){while(scanf("%d%d",&n,&k),n!=-1&&k!=-1){int sum=0;for(int i=1;i<=n;i++){scanf("%d",&v[i]);sum+=v[i];}if(sum<k)printf("-1\n");else{int low=1,high=n;//二分分成的組數 while(low<=high){int mid=(low+high)/2;//如果此時組數滿足條件,減少組數,否則增加 if(getmax(mid))high=mid-1;else low=mid+1;}printf("%d\n",r);}}}
5.3.7 HDU2688 Rotate
一開始是一個類似於用樹狀數組求逆序對的操作,之後的修改演算法用暴力演算法就可以了,統計那一段中比第一個數大的數和小的數,修改res就可以了,res+=less-more;
#include <cstdio>#include <string.h>using namespace std;typedef __int64 LL;const int MAXN=3000010,MAXA=10010;int n,m,f[MAXN],s,e,tmp,mor,les;LL res;char op,c;LL a[MAXA];int lowbit(int x){return x&-x;}void modify(int x){while(x<MAXA)a[x]++,x+=lowbit(x);}LL sum(int x){LL s=0;while(x>0)s+=a[x],x-=lowbit(x);return s;} inline void scan(int &x){while(c=getchar(),c<'0'||c>'9');x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';}int main(){while(scanf("%d",&n)!=EOF){res=0;memset(a,0,sizeof a);for(int i=1;i<=n;i++){scan(f[i]);modify(f[i]);res+=sum(f[i]-1);}scan(m);while(m--){scanf(" %c",&op);if(op=='Q'){printf("%I64d\n",res);}else if(op=='R'){scan(s);scan(e);s++,e++;if(s>e)tmp=s,s=e,e=tmp;tmp=f[s],mor=0,les=0;for(int i=s+1;i<=e;i++){if(f[i]>tmp)mor++;else if(f[i]<tmp)les++;f[i-1]=f[i]; }f[e]=tmp;res+=les-mor;}}}return 0;}
5.3.8 HDU3030 表示沒看懂題..貼大牛代碼的..罪過..