2017年10月12日训练总结

这次训练总结是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上再打几场比赛热热身,为下周的比赛做好准备。

每天都比昨天进步,这就是成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值