先放模板。。。
#define lowbit(x) (x) & (-x) const int N=1005; const int M=1e9+7; //【修改点(向上),求区间(向下)】の模板 int dp[N][N],c[NN][N]; int a[N],r[N],w[N]; bool cmp(int b,int c) { return a[b]<a[c]; } void update(int i,int j,int value){ while(i<=NN){ //这个NN必须足够大(N*2以上) c[i][j]=(c[i][j]+value%M)%M; i+=lowbit(i); } } int sum(int i,int j){ //i不能<=0,因此有些题目要平移值 int s=0; while(i>0){ s=(s+c[i][j])%M; i-=lowbit(i); } return s%M; } int main(){ int t,cnt=0; cin>>t; while(t--){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); memset(c,0,sizeof(c)); memset(dp,0,sizeof(dp)); for(int i=0;i<=n;++i) r[i]=i; //存下标 sort(r+1,r+n+1,cmp); //下标根据a[]的大小排序 for(int i=1;i<=n;++i) w[r[i]]=i; //把a[]哈希一下(原本400,500,600的会变成1,2,3) for(int i = 1; i <= n; ++i) { dp[w[i]][1]=1; update(w[i],1,1); for(int j=2;j<=m;++j) { dp[w[i]][j]=sum(w[i]-1,j-1); //表示w数组到i之前末位小于w[i]的长度为j-1的子序列个数,即Σdp[k][j-1](1<=k<w[i]) update(w[i], j, dp[w[i]][j]);//把dp[w[i]][j]放进对应的位置来 } } int ans = 0; //for(int i=1;i<=n;++i) ans=(ans+dp[i][m])%M; 可以这样写,或者sum(n,m) printf("Case #%d: %d\n",++cnt,sum(n,m) % M); } return 0; }
我也想过写成:
for(int j=2;j<=m;++j){ dp[id][j]=dp1[id-1][j-1]; dp1[id][j]+=dp1[id-1][j]; }
但id的值可能会跳跃的,比如上面的r[]={1,3,5,2,4},算第三个dp[5][j]+=dp1[4][j-1],但此时还没有dp1[4][]的值,所以不能累加。这时就体现出了树状数组的作用。
【求前缀和为k的下标位置,和sum()刚好相反】
int find(int k){ int cnt = 0, ans = 0; for (int i = 20; i >= 0; --i){ //2^20大于50000 ans += (1 << i); if (ans >= n || cnt + c1[ans] >= k) ans -= (1 << i); else cnt += c1[ans]; } return ans+1; }【修改点的值,求区间最值】
void update2(int ii,int val){ //修改点 w[ii]=val; for(int i=ii; i<=n+5; i+=lowbit(i)){ if(val>c2[i]) c2[i]=val; else break; } } int query(int l, int r){ int s=w[r];//上边界 while (l!=r){ for(r-=1;r-lowbit(r)>=l;r-=lowbit(r)){ s=max(s,c2[r]);//注意计算区间,不要夸区间 } s=max(s,w[r]); //下边界 } return s; }
【POJ 2155】 http://poj.org/problem?id=2155 二维矩阵树状数组(修改区间,求点的值)
const int N=1005,NN=3000; const int M=1e9+7; int dp[N][N],c1[NN][NN]; int n,m,a,b,c,d; void update(int ii,int jj,int val){ //修改区间(向下修改) for(int i=ii; i>0; i-=lowbit(i)){ for(int j=jj; j>0; j-=lowbit(j)){ c1[i][j]=(c1[i][j]+val); } } } int sum(int ii,int jj){ //求某点的值(向上统计) int s=0; for(int i=ii; i<=NN; i+=lowbit(i)){ for(int j=jj; j<=NN; j+=lowbit(j)){ s+=c1[i][j]; } } return s; } int main(){ int t; scanf("%d",&t); while(t--){ memset(c1,0,sizeof(c1)); char cc[2]; scanf("%d%d",&n,&m); for(int i=0;i<m;++i){ scanf("%s",&cc); if(cc[0]=='C'){ scanf("%d%d%d%d",&a,&b,&c,&d); a++;b++;c++;d++; update(a-1,b-1,1); update(a-1,d,-1); update(c,b-1,-1); update(c,d,1); } else{ scanf("%d%d",&a,&b); a++;b++; printf("%d\n",sum(a,b)%2); } } printf("\n"); } return 0; }
【POJ2299】 求至少交换几次相邻元素使得数列递增(很难想到可以用树状数组做)http://poj.org/problem?id=2299
http://blog.csdn.net/lyy289065406/article/details/6647346
本质是冒泡排序,但却还可以用归并排序求解。。。http://www.cnblogs.com/gj-Acit/archive/2013/08/10/3250525.html
const int N=500005,NN=1500005; const int M=1e9+7; ll c1[NN]; int n,m; ll a; void update(ll ii,ll val){ //修改点 for(int i=ii; i<=NN; i+=lowbit(i)){ c1[i]=(c1[i]+val); } } int sum(ll ii){ //求区间 ll s=0; for(int i=ii; i>0; i-=lowbit(i)){ s+=c1[i]; } return s; } int main(){ int n; while(cin>>n&&n){ memset(c1,0,sizeof(c1)); ll s=0; for(int i=0;i<n;++i){ cin>>a; a++; s+=i-sum(a-1); update(a,1); } cout<<s<<endl; } return 0; }
【POJ3321】 http://poj.org/problem?id=3321 (修改点,求区间值)
(题意转)给你一颗苹果树,树的主干设为1,每一个分支设为一个数,一直到N,代表这颗苹果树。每个分支上面只能最多有一个苹果,也就是一个枝子上面不可能有两个苹果,另外注意一点,不要把苹果树想象成二叉树,苹果树每个节点可以分出很多叉,应该是多叉树。
输入是叉之间的关系,
1 2
1 3
就是主干上面两个叉分别是2 和3.
下面是两种操作,Q 和C
C j 的意思是如果 j 这个枝子上面有苹果就摘下来,如果没有,那么就会长出新的一个
Q j 就是问 j 这个叉上面的苹果总数。
http://www.cnblogs.com/Jason-Damon/archive/2012/03/02/2376471.html
const int N=100005,NN=300005; const int M=1e9+7; int c1[NN]; vector<vector<int> > v(N); //vector<int> v[N]; 写成这样就会超时?? int f; int y[N],st[N],ed[N]; void update(int ii,int val){ for(int i=ii; i<=NN; i+=lowbit(i)){ c1[i]=(c1[i]+val); } } int sum(int ii){ int s=0; for(int i=ii; i>0; i-=lowbit(i)){ s+=c1[i]; } return s; } void dfs(int p){ //重新定义每个节点的标号,确定子树节点范围(关于这个的理解看链接) st[p]=++f; for(int i=0;i<v[p].size();++i){ dfs(v[p][i]); } ed[p]=f; } int main(){ int n,a,b; scanf("%d",&n); for(int i=0;i<n-1;++i){ scanf("%d %d",&a,&b); v[a].push_back(b); } f=1; //下标从2开始 dfs(1); int m; scanf("%d",&m); for(int i=0;i<m;++i){ char c[2]; scanf("%s %d",c,&a); if(c[0]=='C'){ if(y[a]==0){ update(st[a],-1); y[a]=1; } else{ update(st[a],1); y[a]=0; } } else if(c[0]=='Q'){ printf("%d\n",sum(ed[a])-sum(st[a]-1)+ed[a]-st[a]+1); //初始是都有苹果的 } } return 0; }
hdu3450 lower_bound离散化大数值 http://acm.hdu.edu.cn/showproblem.php?pid=3450
给一些数,问有多少集合,满足相邻的数之间的差的绝对值小于d。
const int N=100005,NN=300005; int x[N],y[N]; int c1[NN]; void update(int ii,int val){ //修改点 for(int i=ii; i<=NN; i+=lowbit(i)){ c1[i]=(c1[i]+val)%9901; } } int sum(int ii){ //求区间 int s=0; for(int i=ii; i>0; i-=lowbit(i)){ s=(s+c1[i])%9901; } return s; } int main(){ int n,m; while(scanf("%d%d",&n,&m)==2){ memset(c1,0,sizeof c1); for(int i=1;i<=n;++i){ scanf("%d",&x[i]); y[i]=x[i]; } sort(y+1,y+n+1); //用y数组来离散化,避免值太大 for(int i=1;i<=n;++i){ int p=lower_bound(y+1,y+n+1,x[i]-m)-y; //取下界 int q=upper_bound(y+1,y+n+1,x[i]+m)-y-1; //取上界 //【注意了!!结果a-b是负数的话要写成((a-b)%mod+mod)%mod,不能省略】 int s=((sum(q+1)-sum(p-1+1))%9901+9901)%9901; int h=lower_bound(y+1,y+n+1,x[i])-y; update(h+1,(s+1)%9901); } printf("%d\n",((sum(n+1)-(n%9901))%9901+9901)%9901); } return 0; }
hdu5592 给出数组a[],a[i]表示b[]的前i个数的逆序对个数,让你还原这个数组b[] http://acm.hdu.edu.cn/showproblem.php?pid=5592
const int N=50005,NN=50005; int x[N]; int y[N],z[N],w[N]; int c1[NN],c2[NN]; int n; void update(int ii,int val){ //修改点 for(int i=ii; i<=n+5; i+=lowbit(i)){ c1[i]=(c1[i]+val); } } int find(int k){ //【求前缀和为k的下标位置。和sum()刚好相反】 int cnt = 0, ans = 0; for (int i = 20; i >= 0; --i){ //2^20大于50000 ans += (1 << i); if (ans >= n || cnt + c1[ans] >= k) ans -= (1 << i); else cnt += c1[ans]; } return ans+1; } int main(){ int t; scanf("%d",&t); while(t--){ memset(c1,0,sizeof(c1)); memset(z,0,sizeof(z)); scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&x[i]); update(i,1); } for(int i=n;i>=2;--i){ int p=x[i]-x[i-1]; y[i]=find(i-p); update(y[i],-1); z[y[i]]=1; } for(int i=1;i<=n;++i){ if(z[i]==0) y[1]=i; } for(int i=1;i<=n-1;++i) printf("%d ",y[i]); printf("%d\n",y[n]); } return 0; }