这次训练总结是10月9日到10月2日。
在上周AK树状数组专题以后,开始了线段树题目的AC之旅。
又看了一遍线段树的ppt和课件,还看了饶齐博客上有关线段树的部分(由于时间关系,只看了一半),学会了线段树的单点更新区间查询、区间set整体查询、区间add区间求和。能够独立解决涂色、贴海报等一些简单的问题,但是,稍微难一点的题目就要参考相关的题解、博客,然后完全理解并独立写出代码。我更加注重了思考的重要性:拿过来一个题,先思考,怎样去解决,用线段树维护什么,要求什么,怎样维护,要注意哪些细节。实在不会就去参考题解,想想为什么思路是这样,它用线段树维护了什么信息,这样维护是否正确,用什么方法求得最终结果,为什么这样,每一个细节都是怎么出来的,去掉行不行,我能不能用另一种方法去解它。完全理解之后,我再根据题目独立写出AC代码。下一次再碰到类似的问题,我一定要快速准确的把代码写出来。
这四天除了自己在cf、hdu和poj上做的几道题目外,在线段树专题中A了5道题。一道是找第i个1update题,一道是第几个人出队题,与上一道题有些类似,一道是贴海报问题,一道是涂色问题(与贴海报问题类似,但是贴海报还用到了离散化,很有趣),一道小明序列(线段树优化dp),还有一道是求区间出现数目最多的数出现了几次。
最后一道题想不出用线段树怎么解,但是想到了一个树状数组的思路,上网查了一下全部都是用线段树做的,于是我用树状数组把它做出来了。。。(感觉树状数组更简单啊)。。。
题目:HDU 1806
思路:树状数组+离线
因为给定的序列是不下降的,但是有负数,先加上100000变成正数,再用数组记录每一个数第一次和最后一次出现的位置。对询问把询问区间化成区间左端点是那个数出现的第一个位置,区间右端点是前一个数出现的最后一个位置,同时初始化ans数组记录最终结果。这样离线的时候只需要遍历所有数的最后一次出现的位置,然后用树状数组维护,右端点等于i就取最大ans即可。需要注意,初始化的时候如果区间内只有一个数或两个数,直接求最大长度即可,对询问区间做标记,离线的时候不做处理。
下面是我用树状数组的AC代码:
#include<iostream>
#include<cmath>
#include<string>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdio>
#include<map>
using namespace std;
int lb(int i){return i&(-i);}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int max(int x,int y){if(x>y)return x;return y;}
const int mx=100010;
int c[mx<<1],vs[mx];
int ans[mx],d[mx];
struct node{
int fir,las;
}a[mx<<1];
struct nodd{
int l,r;
int ind;
int f;
bool operator<(const nodd &qq)const{return r<qq.r||(r==qq.r&&l<qq.l);}
}b[mx];
void add(int i,int d)
{
while(i>0)
{
c[i]=max(c[i],d);
i-=lb(i);
}
}
int query(int i,int ii)
{
int sum=0;
while(i<=ii)
{
sum=max(c[i],sum);
i+=lb(i);
}
return sum;
}
int m,n,q;
int main()
{
int i,j,k,h,T;
int aa,bb,x,y;
while(scanf("%d",&n)&&n)
{
scanf("%d",&q);
memset(c,0,sizeof(c));
memset(ans,0,sizeof(ans));
scanf("%d",&d[1]);d[1]+=100000;
a[d[1]].fir=1;
for(i=2;i<=n;i++){scanf("%d",&d[i]);
d[i]+=100000;
if(d[i]!=d[i-1]){a[d[i-1]].las=i-1;a[d[i]].fir=i;}
}
a[d[n]].las=n;
//for(i=1;i<=n;i++) cout<<d[i]<<" "<<a[d[i]].fir<<" "<<a[d[i]].las<<endl;
for(i=1;i<=q;i++)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[i].ind=i;
x=a[d[b[i].l]].las;
y=a[d[b[i].r]].fir;
if(d[b[i].l]==d[b[i].r]) {ans[i]=b[i].r-b[i].l+1;b[i].f=0;}
else if(x==y-1) {ans[i]=max(x-b[i].l+1,b[i].r-y+1);b[i].f=0;}
else {ans[i]=max(x-b[i].l+1,b[i].r-y+1);b[i].l=x+1,b[i].r=y-1;b[i].f=1;}
}
sort(b+1,b+q+1);
j=1;
for(i=1;i<=n;i++)
{
k=a[d[i]].fir;
i=a[d[i]].las;
aa=i-k+1;
add(i,aa);
while(j<=q&&b[j].f==0) j++;
while(j<=q&&b[j].r==i)
{
ans[b[j].ind]=max(ans[b[j].ind],query(b[j].l,i));
j++;
while(j<=q&&b[j].f==0) j++;
}
if(j>q) break;
}
for(i=1;i<=q;i++) printf("%d\n",ans[i]);
}
return 0;
}
虽然线段树还有很多知识要学,不过下周就要比赛了,从明天开始我去看KMP,虽然之前看过,不过也只是看个最基础的模板,线段树这一块就先交给我的小伙伴了。在比赛之前在cf上再打几场比赛热热身,为下周的比赛做好准备。
每天都比昨天进步,这就是成功。