这一次训练是9月25日到9月28日。
这一段时间专攻树状数组专题,并取得了一些收获。
树状数组之前总结过,就是经典三种查询的题型和求逆序数问题。现在又发现一些区间条件更新条件查询也能做。树状数组可以开多维来解决问题,也可以用不常规的写法求一些特殊条件的问题(例如城市和农村建路、南宁网络赛的最长子序列权值和)都可以用树状数组解决。也发现了它可以求最长上升序列。这三天看了很多博客上的资料,主要是看树状数组的题型和解题思路及代码实现。发现常规题中除了那三种基本查询问题以外,还有离线处理。我感觉树状数组剩下的几道相对比较难的题都应该和离线处理有关。基础的模板题已经没有什么问题了,三天都A的差不多。
对于一些区间覆盖的问题,我还不清楚用树状数组怎么解决。还有一些用树状数组存奇特东西的写法居然也能A,挺神奇的。从晚上回宿舍一直在想一道离线+树状数组的题,思路也不是很难,把区间右端点从小到大排序,再根据条件add、query就可以了。(做这道题差点忘了时间,忘了写博客。。。),下面给出这道题:
Problem N
Time Limit : 4000/2000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 1 Accepted Submission(s) : 1
1 5 2 3 1 2 5 4 1 5 2 4
1 2
思路:树状数组+离线,区间右端点从小到大排序后,从第一个人开始插入。当插入第i个人时,与它的编号相差1的人都会被他代替,就把那两个人删掉就行。插入到等于右端点的人时,查询多少组并存到ans数组里。
代码:
#include<iostream>
#include<cmath>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
#include<map>
#include<cstdio>
using namespace std;
const int mx=100010;
const int inf=0x7fffffff;
const double PI=acos(-1.0);
int max(int x,int y){if(x>y) return x;return y;}
int n,m,ff;
struct node{
int x;int y;int ind;
bool operator<(const node &a)const {return y<a.y;}
}b[mx];
struct no{
int v;int ind;
bool operator<(const no &a)const {return v<a.v;}
}a[mx];
int c[mx];
int ans[mx];
map<int,int>mp;
int lb(int i){return i&(-i);}
int add(int i,int d)
{
while(i<mx) {
c[i]+=d;
i+=lb(i);
}
}
int query(int i)
{
int sum=0;
while(i>0){
sum+=c[i];
i-=lb(i);
}
return sum;
}
int main()
{
int t,i,j,x,y;
char ch[2];
int d;
int flag;
while(scanf("%d",&t)==1)
{
while(t--){
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i].v);
//add(a[i].v,1);
mp[a[i].v]=i;
}
//map[n+2]=n+5;
//map[1]=-1;
//sort(a+1,a+1+n);
for(i=1;i<=m;i++)
{
scanf("%d%d",&b[i].x,&b[i].y);
b[i].ind=i;
}
sort(b+1,b+1+m);
j=1;
int ss;
for(i=1;i<=n;i++)
{
add(i,1);
if(a[i].v>1&&mp[a[i].v-1]<i) {add(mp[a[i].v-1],-1);}
if(a[i].v<n&&mp[a[i].v+1]<i) {add(mp[a[i].v+1],-1);}
while(j<=m&&b[j].y==i) {
int sum1=query(b[j].y)-query(b[j].x-1);
ans[b[j].ind]=sum1;
j++;
}
if(j==m+1) break;
}
for(i=1;i<=m;i++) printf("%d\n",ans[i]);
//puts("");
}
}
return 0;
}
接下来几天争取早日把难题都清掉,尽多的熟悉树状数组的各种用法,锻炼思维能力,争取以后遇到树状数组的中下等题目能快速A掉。
清完树状数组后,再去各网站找几个相关题目做做,然后就开始线段树,这两个重要的知识点一定要熟练。