Abandon の 线段树【专辑】(长期更新)

  先从zkw大神的《统计与力量》感受了zkw线段树的优美(在此先ym 3分钟……),但是自己还是不能深入理解;后来又看了NotOnlySuccess的线段树,虽然是用递归形式但从优美程度来讲一点儿也不差(zkw的非递归自己在拓展方面很难伸展,功力还不够……),我的线段树主要就是受这两位大牛的风格影响了~~~然后练习呀什么的基本是跟着HH神(NotOnlySuccess)的【完全版】线段树来的,然后在其他地方看到的很好的题也自己加了进来。

==========================================================================================================================================  

  对于各类线段树问题来说,

  结点中主要有两种需要维护的数据,一个是标记,一个是统计。 

  主要有两种维护操作,一种是标记下放(懒惰标记,用于区间修改),一种是统计汇总(用于区间查询)。

  可以看出这里我的建树方式采用的是zkw的满二叉树的形式,虽然浪费一些空间(?)但是在点区间对应查找和调试上都非常方便。

==========================================================================================================================================

单点更新:最最基础的线段树,只更新叶子节点,然后把信息用PointUpdate(int n, int V) || PointAdd(int n, int V)函数更新上来。

 

  ♠HDU 1166 敌兵布阵 (线段树入入入门题 || 第一个线段树程序) 

HDU 1166
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <climits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <string>
#include <cstring>

using namespace std;

typedef long long ll;
const double EPS = 1e-11;

void Swap(int &a,int &b){   int t=a;a=b;b=t; }
int Max(int a,int b)    {   return a>b?a:b;  }
int Min(int a,int b)    {   return a<b?a:b;  }

//zkw线段树模板  询问[1,n]区间(初始化时右边区间已经自动扩大)
const int MAX=50005;
struct SegmentTree
{
    int value;
    SegmentTree()
    {
        value=0;
    }
};

int M;
SegmentTree T[MAX*4];

void BuildTree(int n)                           //初始化线段树
{
    for (M=1;M<=n+2;M<<=1);

    //problem special operator
    for (int i=M+1;i<=M+n;i++)
        scanf("%d",&T[i].value);

    for (int i=M-1;i>0;i--)
        T[i].value=T[2*i].value+T[2*i+1].value;
}

void PointUpdate(int n, int V)                  //单点更新
{
    for (T[n+=M].value=V,n>>=1;n;n>>=1)
        T[n].value=T[2*n].value+T[2*n+1].value;
}

void PointAdd(int n, int V)                     //单点增加,减的话把V变-即可
{
    for (T[n+=M].value+=V,n>>=1;n;n>>=1)
        T[n].value=T[2*n].value+T[2*n+1].value;
}

int Query(int s,int t)                          //区间、点询问
{
    int sum=0;
    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
    {
        if (~s&1)   sum+=T[s^1].value;
        if (t&1)    sum+=T[t^1].value;
    }
    return sum;
}

int main()
{
    int t;
    int casenum=1;
    scanf("%d",&t);
    while(t--)
    {
        printf("Case %d:\n",casenum++);
        int n;
        scanf("%d",&n);
        int a,b;
        BuildTree(n);
        char c[6];
        //scanf("%*c");
        while(scanf("%s",c))
        {
            if (strcmp(c,"End")==0)
                break;
            if (strcmp(c,"Query")==0)
            {
                scanf("%d%d",&a,&b);
                printf("%d\n",Query(a,b));
            }
            else if (strcmp(c,"Add")==0)
            {
                scanf("%d%d",&a,&b);
                PointAdd(a,b);
            }
            else
            {
                scanf("%d%d",&a,&b);
                PointAdd(a,-b);
            }
        }
    }
    return 0;
}

 

  ♠POJ 2828 Buy Tickets ★(好题,逆推思路)

  问题抽象求第K小点

  思路:分析发现, 第i个人对他后面的人的位置都有可能有影响,但对他前面的人的位置一定没有影响。所以,我们可倒着插队。从后向前,先操作第n个人,当操作第i个人时,我们将他插在第pos[i]+1个空处。因为此时区间内的空处,是正着插队时前面奶牛所占的位置。对于求第K点,线段树时跑不过SBT。(嗯~这题可以SBT的......感觉线段树在这儿就是起了一个维护前缀和还有二分搜索的作用......)

POJ 2828
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <climits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <string>
#include <cstring>

using namespace std;

typedef long long ll;
const double EPS = 1e-11;

void Swap(int &a,int &b){   int t=a;a=b;b=t; }
int Max(int a,int b)    {   return a>b?a:b;  }
int Min(int a,int b)    {   return a<b?a:b;  }

//zkw线段树模板  询问[1,n]区间(初始化时右边区间已经自动扩大)
const int MAX=200005;
struct SegmentTree
{
    int value;
    SegmentTree()
    {
        value=1;
    }
};

int M;
SegmentTree T[MAX*4];
int path[MAX*4];

void BuildTree(int n)                           //初始化线段树
{
    for (int i=0;i<MAX*4;i++)    //一开始这个范围没弄对WA一次……
        T[i].value=1;
    for (M=1;M<=n+2;M<<=1);
    for (int i=M-1;i>0;i--)
        T[i].value=T[2*i].value+T[2*i+1].value;
}

void Insert(int p,int v,int l,int r,int rt)      //special operator
{
    if (l==r)
    {
        T[rt].value=0;
        path[rt]=v;
        return ;
    }

    int mid=(l+r)>>1;
    if (p<=T[rt<<1].value)  Insert(p,v,l,mid,rt<<1);
    else
        Insert(p-T[rt<<1].value,v,mid+1,r,rt<<1|1);
    T[rt].value=T[rt<<1].value+T[rt<<1|1].value;
    return ;
}

int main()
{
    int n;
    int p[MAX],v[MAX];
    while(scanf("%d",&n)!=EOF)
    {
        memset(path,0,sizeof(path));
        BuildTree(n);
        for (int i=0;i<n;i++)
        {
            scanf("%d%d", &p[i], &v[i]);
        }
        for (int i=n-1;i>=0;i--)
            Insert(p[i]+1,v[i],1,M,1);
        printf("%d", path[M]);
        for (int i=1;i<n;i++)
            printf(" %d", path[i+M]);
        printf("\n");
    }
    return 0;
}

 

  ♠HDU 4302 Holedox Eating ★(2012 Multi-University Training Contest 1)

  问题抽象求特殊区间最大点、最小点

  思路:每次左边右边寻找距离当前位置最近的点,则用线段树求[0,now]区间最大数,求[now,l]区间最小的数维护即可。 

HDU 4302
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <set>
 11 #include <map>
 12 #include <algorithm>
 13 #include <string>
 14 #include <cstring>
 15 #define MID(x,y) ( ( x + y ) >> 1 )
 16 #define FOR(i,s,t) for(int i=s; i<t; i++)
 17 
 18 using namespace std;
 19 
 20 typedef long long LL;
 21 
 22 const int N=100005;
 23 int M;
 24 int sum[N<<2];
 25 
 26 void PushUp(int rt)
 27 {
 28     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 29 }
 30 
 31 void BuildTree(int n)
 32 {
 33     for (M=1;M<=n+2;M<<=1);
 34     for (int i=1;i<N<<2;i++)
 35         sum[i]=0;
 36 }
 37 
 38 void Update(int n,int v)
 39 {
 40     for (sum[n+=M]+=v,n>>=1;n;n>>=1)
 41         PushUp(n);
 42 }
 43 
 44 int Qsum(int s,int t,int l,int r,int rt)
 45 {
 46     if (s<=l && r<=t)
 47     {
 48         return sum[rt];
 49     }
 50 
 51     int mid=MID(l,r);
 52     int ans=0;
 53     if (s<=mid) ans+=Qsum(s,t,l,mid,rt<<1);
 54     if (mid<t)  ans+=Qsum(s,t,mid+1,r,rt<<1|1);
 55     return ans;
 56 }
 57 
 58 int Querymax(int s,int l,int r,int rt)
 59 {
 60     if (l==r)
 61     {
 62         return l;
 63     }
 64 
 65     int mid=MID(l,r);
 66     if (sum[rt<<1]>=s)
 67     {
 68         return Querymax(s,l,mid,rt<<1);
 69     }
 70     else return Querymax(s-sum[rt<<1],mid+1,r,rt<<1|1);
 71 }
 72 
 73 int Querymin(int s,int l,int r,int rt)
 74 {
 75     if (l==r)
 76     {
 77         return l;
 78     }
 79 
 80     int mid=MID(l,r);
 81     if (sum[rt<<1|1]>=s)
 82     {
 83         return Querymin(s,mid+1,r,rt<<1|1);
 84     }
 85     else return Querymin(s-sum[rt<<1|1],l,mid,rt<<1);
 86 }
 87 
 88 int main()
 89 {
 90     //freopen("test.in","r+",stdin);
 91 
 92     int t,caseo=1;
 93     scanf("%d",&t);
 94     while(t--)
 95     {
 96         printf("Case %d: ",caseo++);
 97         int ans=0;
 98         int l,n;
 99         scanf("%d%d",&l,&n);
100         BuildTree(l);
101         int h=0;
102         int dir=0;
103         for (int i=0;i<n;i++)
104         {
105             int p;
106             scanf("%d",&p);
107             if (p==0)
108             {
109                 int num;
110                 scanf("%d",&num);
111                 Update(num,1);
112             }
113             else
114             {
115                 if (sum[1]==0)  continue;
116                 int a1=Qsum(0,h,0,M-1,1);
117                 int a2=Qsum(h,M,0,M-1,1);
118                 if (a1==0)
119                 {
120                     int b2=Querymin(a2,0,M-1,1);
121                     dir=1;
122                     ans+=b2-h;
123                     h=b2;
124                     Update(b2,-1);
125                 }
126                 else if (a2==0)
127                 {
128                     int b1=Querymax(a1,0,M-1,1);
129                     dir=-1;
130                     ans+=h-b1;
131                     h=b1;
132                     Update(b1,-1);
133                 }
134                 else
135                 {
136                     int b1=Querymax(a1,0,M-1,1);
137                     int b2=Querymin(a2,0,M-1,1);
138                     if (b2-h>h-b1)
139                     {
140                         ans+=h-b1;
141                         h=b1;
142                         dir=-1;
143                         Update(b1,-1);
144                     }
145                     else if (b2-h<h-b1)
146                     {
147                         ans+=b2-h;
148                         h=b2;
149                         dir=1;
150                         Update(b2,-1);
151                     }
152                     else
153                     {
154                         ans+=b2-h;
155                         if (dir==1)
156                         {
157                             h=b2;
158                             Update(b2,-1);
159                         }
160                         else
161                         {
162                             h=b1;
163                             Update(b1,-1);
164                         }
165                     }
166                 }
167             }
168         }
169         printf("%d\n",ans);
170     }
171     return 0;
172 }

 

  ♠POJ 3264 Balanced Lineup (离线RMQ,更新都不用,轻松1Y,简单题=。=)

POJ 3264
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <climits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <algorithm>
#include <string>
#include <cstring>

using namespace std;

typedef long long ll;
const double EPS = 1e-11;

void Swap(int &a,int &b){   int t=a;a=b;b=t; }
int Max(int a,int b)    {   return a>b?a:b;  }
int Min(int a,int b)    {   return a<b?a:b;  }

//zkw线段树模板  询问[1,n]区间(初始化时右边区间已经自动扩大)
const int N=50005;
struct SegmentTree
{
    int max,min;
    SegmentTree()
    {
        max=0;
        min=INT_MAX;
    }
};

int M;
SegmentTree T[N*4];

void BuildTree(int n)                           //初始化线段树
{
    for (M=1;M<=n+2;M<<=1);

    //special operator
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&T[i+M].max);
        T[i+M].min=T[i+M].max;
    }

    for (int i=M-1;i>0;i--)
    {
        T[i].max=Max(T[2*i].max,T[2*i+1].max);
        T[i].min=Min(T[2*i].min,T[2*i+1].min);
    }
}

int QueryMax(int s,int t)
{
    int maxc=-1;
    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
    {
        if (~s&1)   maxc=Max(maxc,T[s^1].max);
        if (t&1)    maxc=Max(maxc,T[t^1].max);
    }
    return maxc;
}

int QueryMin(int s,int t)
{
    int minc=INT_MAX;
    for (s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
    {
        if (~s&1)   minc=Min(minc,T[s^1].min);
        if (t&1)    minc=Min(minc,T[t^1].min);
    }
    return minc;
}

int main()
{
    int n,q;
    scanf("%d%d",&n,&q);
    BuildTree(n);
    for (int i=0;i<q;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",QueryMax(a,b)-QueryMin(a,b));
    }
    return 0;
}

 

  ♠HDU 4288 Coder ★(2012 ACM/ICPC Asia Regional Chengdu Online)

  问题抽象分组线段树求和

  思路:离线(离散化+排序)维护5颗线段树。sum[rt][5]的每棵树表示区间的数以该区间左端为起点mod 5的余数,cnt[rt]表示区间数的数目。一开始不知道怎么动态地维护插入、删除数据的位置的模5的余数,比如一开始插入1、3、5,5是要求的,但要是再插入个2变成1、2、3、5,那么就变成3了。。。这个让我想了好久,后来经过一些提示终于想到了思路:每个叶节点的值都附在sum[rt][0]里,即上面说的,sum[rt][i]表示以该区间左端点为起点mod 5的余数。那么在向上统计汇总时怎么转化呢?

  答案是:sum[结点][i]=sum[左儿子][i]+sum[右儿子][((i+5)-cnt[左儿子]%5)%5]

  什么意思呢?从sum的意义出发,左儿子的区间左端点和父节点是一样的,所以他们的余数等价;然而需要把右儿子的左端点与父节点等价起来。设父区间左端点为a,则右儿子区间左端点即为a+cnt[左儿子]。若右儿子(pos-a)%5==i,则把它放到父区间(pos-a-cnt[])%5== i-cnt[]%5== (保证大于等于0小于5) ((i+5)-cnt[]%5)%5。

  另外要注意这里离散化的方法~~~lower_bound()函数可以很方便的找到数在原数组中的位置。(或者自己写一个二分也可以。。。) 

HDU 4288
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <iomanip>
 6 #include <climits>
 7 #include <vector>
 8 #include <queue>
 9 #include <map>
10 #include <algorithm>
11 #include <string>
12 #include <cstring>
13 #define MID(x,y)    ((x+y)>>1)
14 
15 using namespace std;
16 typedef long long LL;
17 
18 const int N=100005;
19 int M;
20 LL sum[N<<2][5];
21 int cnt[N<<2];
22 
23 void BuildTree(int n)
24 {
25     for (M=1;M<=n+2;M<<=1);
26     for (int i=1;i<N<<2;i++)
27     {
28         for (int j=0;j<5;j++)
29             sum[i][j]=0;
30         cnt[i]=0;
31     }
32 }
33 
34 void PushUp(int rt)
35 {
36     cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1];
37     for (int i=0;i<5;i++)
38         sum[rt][i]=sum[rt<<1][i]+sum[rt<<1|1][((i+5)-cnt[rt<<1]%5)%5];
39 }
40 
41 void Update(int s,int num,int v,int l,int r,int rt)
42 {
43     if (l==s && r==s)
44     {
45         sum[rt][0]+=v*num;
46         cnt[rt]+=num;
47         return;
48     }
49 
50     int mid=MID(l,r);
51     if (s<=mid) Update(s,num,v,l,mid,rt<<1);
52     else Update(s,num,v,mid+1,r,rt<<1|1);
53     PushUp(rt);
54 }
55 
56 char str[100005][5];
57 int pri[100005];
58 int a[100005];
59 
60 int main()
61 {
62     //freopen("test.in","r+",stdin);
63     int n;
64     while(scanf("%d",&n)!=EOF)
65     {
66         int tot=0;
67         for (int i=0;i<n;i++)
68         {
69             scanf("%s",str[i]);
70             if (str[i][0]!='s')
71             {
72                 scanf("%d",&pri[i]);
73                 a[tot++]=pri[i];
74             }
75         }
76         sort(a,a+tot);
77         BuildTree(tot);
78         for (int i=0;i<n;i++)
79         {
80             if (str[i][0]=='a')
81             {
82                 int x=pri[i];
83                 int pos=lower_bound(a,a+tot,x)-a+1;
84                 Update(pos,1,x,1,M,1);
85             }
86             else if (str[i][0]=='d')
87             {
88                 int x=pri[i];
89                 int pos=lower_bound(a,a+tot,x)-a+1;
90                 Update(pos,-1,x,1,M,1);
91             }
92             else
93             {
94                 printf("%I64d\n",sum[1][2]);
95             }
96         }
97     }
98     return 0;
99 }

 

  ♠HDU 4417 Super Mario ★(2012 ACM/ICPC Asia Regional Hangzhou Online)

  问题抽象询问区间内比一个数的权值小的数的个数

  思路:将所有的询问离线读入之后,按H从小到大排序。对于所有的节点也按从小到大排序,然后根据查询的H,将比H小的点加入到线段树,然后就是一个区间和。

  另一个思路:二分+区间第k大(划分树、函数式线段树……)

HDU 4417
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 
 16 const int N=100500;
 17 struct Co
 18 {
 19     int l,r;
 20     int h;
 21     int p;
 22 }co[N];
 23 
 24 struct Po
 25 {
 26     int i;
 27     int h;
 28     friend bool operator < (const Po &a,const Po &b)
 29     {
 30         return a.h>b.h;
 31     }
 32 };
 33 priority_queue <Po, vector<Po> > Q;
 34 Po po[N];
 35 int ans[N];
 36 
 37 bool cmp(Co a,Co b)
 38 {
 39     return a.h<b.h;
 40 }
 41 
 42 int M;
 43 int sum[N<<2];
 44 void PushUp(int rt)
 45 {
 46     sum[rt]=sum[rt<<1]+sum[rt<<1|1];
 47 }
 48 void build(int n)
 49 {
 50     for (M=1;M<=n+2;M<<=1);
 51     for (int i=1;i<N<<2;i++)
 52         sum[i]=0;
 53 }
 54 void Update(int n,int V)
 55 {
 56     for (sum[n+=M]=V,n>>=1;n;n>>=1)
 57         PushUp(n);
 58 }
 59 int query(int s,int t,int l,int r,int rt)
 60 {
 61     if (s<=l && r<=t)
 62     {
 63         return sum[rt];
 64     }
 65 
 66     int mid=MID(l,r);
 67     int res=0;
 68     if (s<=mid) res+=query(s,t,l,mid,rt<<1);
 69     if (mid<t)  res+=query(s,t,mid+1,r,rt<<1|1);
 70     return res;
 71 }
 72 
 73 int main()
 74 {
 75     //freopen("test.in","r+",stdin);
 76 
 77     int t, caseo = 1;
 78     scanf("%d",&t);
 79     while(t--)
 80     {
 81         while(!Q.empty())
 82             Q.pop();
 83         memset(po,0,sizeof(po));
 84         printf("Case %d:\n",caseo ++);
 85         int n,m;
 86         scanf("%d%d",&n,&m);
 87         build(n);
 88         for (int i=0;i<n;i++)
 89         {
 90             scanf("%d",&po[i].h);
 91             po[i].i=i;
 92             Q.push(po[i]);
 93         }
 94 //        while(!Q.empty())
 95 //        {
 96 //            cout<<Q.top().h<<endl;
 97 //            Q.pop();
 98 //        }
 99         for (int i=0;i<m;i++)
100         {
101             scanf("%d%d%d",&co[i].l,&co[i].r,&co[i].h);
102             co[i].p=i;
103         }
104         sort(co,co+m,cmp);
105         for (int i=0;i<m;i++)
106         {
107             while(!Q.empty() && Q.top().h<=co[i].h)
108             {
109                 Po tmp=Q.top();
110                 Update(tmp.i,1);
111                 Q.pop();
112             }
113             ans[co[i].p]=query(co[i].l,co[i].r,0,M-1,1);
114         }
115         for (int i=0;i<m;i++)
116             printf("%d\n",ans[i]);
117     }
118     return 0;
119 }

 

  ♠HDU 4366 Successor ★(2012 Multi-University Training Contest 7)

  问题抽象询问区间内比一个数的first权值大的数中second权值最大的数

  思路:先一遍dfs将树形结构转为线性结构,转化后的每个点对应一个区间,区间内是它所有的子孙。然后问题就转化成了求区间内比一个数的权值大的数中权值最大的数。和上面那题很像了。将所有询问离线读入,按能力值从大到小排序。对于节点也按从大到小排序,然后根据查询点的能力值,将比它能力值大的点都插进线段树中,这样保证了线段树中存的都是满足条件的数(能力比上司高),再在对应区间内找个忠诚值最高的就好了(线段树中维护个max值什么的...)。

  悲剧的是我在dfs爆栈了=.=。。。其实我觉得这个栈溢出的挺正常,毕竟50000个点压进栈什么的...=.=,可别人的为什么不会呀T_T,姿势也差不多呢呀,HDU 4358那道题为什么没爆呀=.=。。。算了,先放着吧,有时间了手动模拟栈再重新做一遍。。。

 

区间更新:(通常这对初学者来说是一道坎)需要用到延迟标记(懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候。(这个阶段zkw线段树的精髓吾尚不能参透T_T......所以就结合用懒惰标记了=。=......)

 

注意
由于是对每个区间的值进行修改,如果我们每次修改都修改到叶子节点,那么修改复杂度就降到了O(n),必然导致TLE,所以要用懒惰标记。{ 注意这里默认的总区间是[1..M]不是HH大牛的[1..n],用错了区间和数组下标会对不上(因为zkw式建树和HH大牛的建树方式不同,zkw式在BuildTree()时建的就是[1,M]的满二叉树,这样建树的好处是下标与区间对应的很工整(详见《统计的力量》),查找会很方便。而HH牛的线段树开的是[1,n]的树)。。。而且要注意初始化时是从M赋值还是M+1,如果从M开始赋值(第0个叶节点开始表示区间1),则总区间为[1..M];如果从M+1开始赋值(用第1个叶节点开始表示区间1->前面还有第0个叶节点),则总区间为[0..M-1]。为了方便且易查错,我的程序就统一从M开始赋值,总区间定为[1,M] }

 

  懒惰标记就是:如果当前的区间被要更新的区间所覆盖,直接就改变当前结点的信息,不用再往下了,所以,每次在更新的时候,只更新一层,把当前结点的信息往下传递一层,以供下一步使用,如果下一步完成了更新,更新也就结束了,没有完成,继续往下传递一层。。。。

 

  ♠POJ 3468 A Simple Problem with Integers (区域修改->懒惰标记 入门题)

POJ 3468
  1 POJ 3468
  2 
  3 #include <iostream>
  4 #include <cstdio>
  5 #include <cstdlib>
  6 #include <cmath>
  7 #include <iomanip>
  8 #include <climits>
  9 #include <vector>
 10 #include <stack>
 11 #include <queue>
 12 #include <set>
 13 #include <map>
 14 #include <algorithm>
 15 #include <string>
 16 #include <cstring>
 17 
 18 using namespace std;
 19 
 20 typedef long long LL;
 21 const double EPS = 1e-11;
 22 
 23 //zkw线段树模板————区间更新
 24 
 25 const int N=100005;                //结点个数
 26 
 27 int M;
 28 __int64 sum[N<<2];                    //M*2,特殊情况M会接近2*N。比如N=1023[1,1023],则M=2048---zkw《统计的力量》
 29 __int64 add[N<<2];
 30 
 31 void PushUp(int rt)             //统计汇总,rt为当前节点
 32 {
 33     sum[rt] = sum[rt<<1] + sum[rt<<1|1];    //如果是区间最值则 sum[n]=max(sum[n*2],sum[n*2+1])
 34 }
 35 
 36 void BuildTree(int n)
 37 {
 38     for (M=1;M<=n+2;M<<=1);            //求出M值(查询区间[1,M])
 39     for (int i=1;i<(N<<2);i++)
 40     {
 41         sum[i]=0;
 42         add[i]=0;
 43     }
 44     for (int i=M;i<M+n;i++)
 45         scanf("%I64d",&sum[i]);
 46     for (int i=M-1;i>0;i--)
 47         PushUp(i);
 48 }
 49 
 50 void PushDown(int rt,int l)        //标记下放,rt为当前节点,l为修改区间长度
 51 {
 52     if (add[rt])
 53     {
 54         add[rt<<1]+=add[rt];
 55         add[rt<<1|1]+=add[rt];
 56         sum[rt<<1]+=add[rt]*(l-(l>>1));
 57         sum[rt<<1|1]+=add[rt]*(l>>1);
 58         add[rt]=0;
 59     }
 60 }
 61 
 62 void Update(int s,int t,int v,int l,int r,int rt)
 63 {
 64     if (s<=l && r<=t)
 65     {
 66         add[rt]+=v;
 67         sum[rt]+=v*(r-l+1);
 68         return ;
 69     }
 70     PushDown(rt,r-l+1);
 71     int m=(l+r)>>1;
 72     if (s<=m)   Update(s,t,v,l,m,rt<<1);
 73     if (m<t)    Update(s,t,v,m+1,r,rt<<1|1);
 74     PushUp(rt);
 75 }
 76 
 77 __int64 Query(int s,int t,int l,int r,int rt)
 78 {
 79     if (s<=l && r<=t)
 80         return sum[rt];
 81     PushDown(rt,r-l+1);
 82     __int64 ans=0;
 83     int m=(l+r)>>1;
 84     if (s<=m)   ans+=Query(s,t,l,m,rt<<1);
 85     if (m<t)    ans+=Query(s,t,m+1,r,rt<<1|1);
 86     return ans;
 87 }
 88 
 89 int main()
 90 {
 91     int n,q;
 92     scanf("%d%d",&n,&q);
 93     BuildTree(n);
 94     for (int i=0;i<q;i++)
 95     {
 96         char c;
 97         int a,b,d;
 98         scanf("%*c%c",&c);
 99         if (c=='Q')
100         {
101             scanf("%d%d",&a,&b);
102             printf("%I64d\n",Query(a,b,1,M,1));
103         }
104         else
105         {
106             scanf("%d%d%d",&a,&b,&d);
107             Update(a,b,d,1,M,1);
108         }
109     }
110     return 0;
111 }

 

  ♠HDU 4031 Attack ★(2011 ACM/ICPC Asia Regional Chengdu Site —— Online Contest

  思路:如果没有防守间隔的限制,那么就是一道裸的线段树区间增减、单点查询问题。防守间隔的限制貌似阻碍了我们直接对区间修改,因为区间中不同点的防守状态不尽相同,就要个个针对,如果这样貌似的话就又退化成了N次单点更新---显然不可取。

  一个重要的思路就是分而治之---把攻击和防守分开考虑。截至当前时间,一个点被成功攻击的次数 = 总攻击数 - 这段时间这个点防御的次数。(具体怎么求防守次数,看了代码就会很好理解的~~~)(因为模板的初始化sum add的范围(1, 2<<N)没弄对WA一次T_T……又返回去把前面的模板修正了下……) 

HDU 4031
  1 #include<cstdio>
  2 #include<cstring>
  3 #define lson l,m,rt<<1
  4 #define rson m+1,r,rt<<1|1
  5 const int N = 20001;
  6 int M;
  7 int sum[N<<2];
  8 int add[N<<2];
  9 
 10 struct pp{
 11     int l,r;
 12 }att[N];
 13 
 14 int defen[N];
 15 
 16 void PushUp(int rt)             //统计汇总,rt为当前节点
 17 {
 18     sum[rt] = sum[rt<<1] + sum[rt<<1|1];    //如果是区间最值则 sum[n]=max(sum[n*2],sum[n*2+1])
 19 }
 20 
 21 void PushDown(int rt,int l)        //标记下放,rt为当前节点,l为修改区间长度
 22 {
 23     if (add[rt])
 24     {
 25         add[rt<<1]+=add[rt];
 26         add[rt<<1|1]+=add[rt];
 27         sum[rt<<1]+=add[rt]*(l-(l>>1));
 28         sum[rt<<1|1]+=add[rt]*(l>>1);
 29         add[rt]=0;
 30     }
 31 }
 32 
 33 void BuildTree(int n)
 34 {
 35     for (M=1;M<=n+2;M<<=1);            //求出M值(查询区间[1,M])
 36     for (int i=1;i<M;i++)
 37     {
 38         sum[i]=0;
 39         add[i]=0;
 40     }
 41     for (int i=M-1;i>0;i--)
 42         PushUp(i);
 43 }
 44 
 45 
 46 void Update(int s,int t,int v,int l,int r,int rt)
 47 {
 48     if (s<=l && r<=t)
 49     {
 50         add[rt]+=v;
 51         sum[rt]+=v*(r-l+1);
 52         return ;
 53     }
 54     PushDown(rt,r-l+1);
 55     int m=(l+r)>>1;
 56     if (s<=m)   Update(s,t,v,l,m,rt<<1);
 57     if (m<t)    Update(s,t,v,m+1,r,rt<<1|1);
 58     PushUp(rt);
 59 }
 60 
 61 int Query(int s,int t,int l,int r,int rt)
 62 {
 63     if (s<=l && r<=t)
 64         return sum[rt];
 65     PushDown(rt,r-l+1);
 66     int ans=0;
 67     int m=(l+r)>>1;
 68     if (s<=m)   ans+=Query(s,t,l,m,rt<<1);
 69     if (m<t)    ans+=Query(s,t,m+1,r,rt<<1|1);
 70     return ans;
 71 }
 72 
 73 int main(){
 74     int t,cases=1,i,j,t0,a,b,n,q;
 75     char s[10];
 76     scanf("%d",&t);
 77     while(t--){
 78 
 79         int tot=0;
 80         memset(defen,0,sizeof(defen));
 81         scanf("%d%d%d",&n,&q,&t0);
 82         att[0].l=att[0].r=0;
 83         BuildTree(n);
 84         printf("Case %d:\n",cases++);
 85         while(q--){
 86             scanf("%s",s);
 87             if(s[0]=='A'){
 88                 scanf("%d%d",&a,&b);
 89                 tot++;
 90                 att[tot].l=a;att[tot].r=b;
 91                 Update(a,b,1,1,M,1);
 92             }
 93             else {
 94                 scanf("%d",&a);
 95                 for(i=0;i<=tot;i++){
 96                     if(a>=att[i].l&&a<=att[i].r){
 97                         defen[a]++;
 98                         i+=t0-1;
 99                     }
100                 }
101                 printf("%d\n",Query(a,a,1,M,1)-defen[a]);
102 
103 
104             }
105         }
106     }
107     return 0;
108 }

 

  ♠HDU 4027 Can you answer these queries? ★(2011 ACM/ICPC Asia Regional Shanghai Site —— Online Contest)

  思路:这道题的难点在于处理平方根时区间不好处理。一开始把思路集中在和的平方根和平方根的和的关系上,果然还是思维不够宽阔呐~这道题如果能想到一个I64整数也最多开平方8次就成1的话就简单了。在区间上加一个域bol[]判断该区间的数是不是都是1了,如果是则不用修改了。这样下来没个点最多被修改8次,不会超时。

HDU 4027
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <algorithm>
 11 #include <string>
 12 #include <cstring>
 13 #define MID(x,y) ( ( x + y ) >> 1 )
 14 #define FOR(i,s,t) for(int i=s; i<t; i++)
 15 
 16 using namespace std;
 17 
 18 //typedef long long ll;
 19 
 20 //zkw线段树模板————区间更新
 21 
 22 const int N=100005;                //结点个数
 23 
 24 int M;
 25 __int64 sum[N<<2];                //M*2,特殊情况M会接近2*N。比如N=1023[1,1023],则M=2048---zkw《统计的力量》
 26 int bol[N<<2];
 27 
 28 void PushUp(int rt)             //统计汇总,rt为当前节点
 29 {
 30     sum[rt] = sum[rt<<1] + sum[rt<<1|1];        //如果是区间最值则 sum[n]=max(sum[n*2],sum[n*2+1])
 31     bol[rt] = min(bol[rt<<1],bol[rt<<1|1]);
 32 }
 33 
 34 void BuildTree(int n)
 35 {
 36     for (M=1;M<=n+2;M<<=1);            //求出M值(查询区间[1,M])
 37     for (int i=1;i<(N<<2);i++)            //初始化属性
 38     {
 39         sum[i]=0;
 40         bol[i]=0;
 41     }
 42 
 43     for (int i=M;i<M+n;i++)
 44     {
 45         scanf("%I64d",&sum[i]);
 46         if (sum[i]==1)
 47             bol[i]=1;
 48     }
 49 
 50     for (int i=M-1;i>0;i--)
 51         PushUp(i);
 52 }
 53 
 54 
 55 void Update(int s,int t,int l,int r,int rt)
 56 {
 57     if (s<=l && r<=t)
 58     {
 59         if (bol[rt]==1)
 60             return ;
 61         else if (l==r)
 62         {
 63             sum[rt]=(__int64)sqrt(double(sum[rt]));
 64             if (sum[rt]==1)
 65                 bol[rt]=1;
 66             return ;
 67         }
 68     }
 69 
 70     int m=MID(l,r);
 71     if (s<=m)   Update(s,t,l,m,rt<<1);
 72     if (m<t)    Update(s,t,m+1,r,rt<<1|1);
 73     PushUp(rt);
 74 }
 75 
 76 __int64 Query(int s,int t,int l,int r,int rt)        //l,r是线段树总区间,s,t是待查询区间,rt为根(一般设为1)
 77 {
 78     if (s<=l && r<=t)
 79         return sum[rt];
 80 
 81     __int64 ans=0;
 82     int m=MID(l,r);
 83     if (s<=m)   ans+=Query(s,t,l,m,rt<<1);
 84     if (m<t)    ans+=Query(s,t,m+1,r,rt<<1|1);
 85     return ans;
 86 }
 87 
 88 int main()
 89 {
 90     //freopen("text.in","r+",stdin);
 91 
 92     int n;
 93     int caseo=1;
 94     while(scanf("%d",&n)!=EOF)
 95     {
 96         printf("Case #%d:\n",caseo++);
 97         BuildTree(n);
 98         int k;
 99         scanf("%d",&k);
100         for (int i=0;i<k;i++)
101         {
102             int t;
103             scanf("%d",&t);
104             if (!t)
105             {
106                 int a,b;
107 
108                 scanf("%d%d",&a,&b);
109                 if (a>b) a^=b, b^=a, a^=b;
110                 Update(a,b,1,M,1);
111             }
112             else
113             {
114                 int a,b;
115 
116                 scanf("%d%d",&a,&b);
117                 if (a>b) a^=b, b^=a, a^=b;
118                 printf("%I64d\n",Query(a,b,1,M,1));
119             }
120         }
121 
122         printf("\n");
123     }
124 
125 
126     return 0;
127 }

  

  ♠POJ 2528 Mayor's posters (坐标离散化->数组hash)

  问题抽象线段插入问题。(通常需要把坐标离散化成“线段”)

 

思路
这题的坐标范围有点儿大(1-10000000),普通线段树肯定会超时+超内存,所以就需要离散化:

  离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来了

  所以离散化要保存所有需要用到的值,排序后,分别映射到1~n,这样复杂度就会小很多很多
  而这题的难点在于每个数字其实表示的是一个单位长度(并非一个点),这样普通的离散化会造成许多错误(包括我以前的代码,poj这题数据奇弱)
  给出下面两个简单的例子应该能体现普通离散化的缺陷:
  例子一:1-10 1-4 5-10
  例子二:1-10 1-4 6-10
  普通离散化后都变成了[1,4][1,2][3,4]
  线段2覆盖了[1,2],线段3覆盖了[3,4],那么线段1是否被完全覆盖掉了呢?
  例子一是完全被覆盖掉了,而例子二没有被覆盖

  为了解决这种缺陷,我们可以在排序后的数组上加些处理,比如说[1,2,6,10]

  如果相邻数字间距大于1的话,在其中加上任意一个数字,比如加成[1,2,3,6,7,10],然后再做线段树就好了.

  PS:这题我写的程序和一个AC程序对拍了N次没错,但交到POJ就是WA。。。哎,算了,留待以后写吧。。。

 

 

  ♠HDU 4325 Flowers (2012 Multi-University Training Contest 3)(坐标离散化->map hash) 

HDU 4325
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 
 16 const int MAXN = 200100;
 17 int M;
 18 int sum[MAXN<<2],add[MAXN<<2];
 19 
 20 void build(int n)
 21 {
 22     for (M = 1; M <= n+2; M <<= 1);
 23     for (int i = 1; i < MAXN << 2; i ++)
 24         sum[i] = 0, add[i] = 0;
 25 }
 26 
 27 void PushDown(int rt,int l)
 28 {
 29     if (add[rt])
 30     {
 31         add[rt<<1] += add[rt];
 32         add[rt<<1|1] += add[rt];
 33         sum[rt<<1] += add[rt] * (l - (l >> 1));
 34         sum[rt<<1|1] += add[rt] * (l >> 1);
 35         add[rt] = 0;
 36     }
 37 }
 38 
 39 void Update(int s,int t,int v,int l,int r,int rt)
 40 {
 41     if (s <= l && r <= t)
 42     {
 43         add[rt] += v;
 44         sum[rt] += v * (r - l + 1);
 45         return ;
 46     }
 47     PushDown(rt, r-l+1);
 48     int mid = MID(l,r);
 49     if (s <= mid) Update(s,t,v,l,mid,rt<<1);
 50     if (mid < t)  Update(s,t,v,mid+1,r,rt<<1|1);
 51 }
 52 
 53 int query(int p,int l,int r,int rt)
 54 {
 55     if (l == p && r == p)
 56     {
 57         return sum[rt];
 58     }
 59 
 60     PushDown(rt,r-l+1);
 61     int mid = MID(l,r);
 62     int res = 0;
 63     if (p <= mid) res += query(p,l,mid,rt<<1);
 64     else res += query(p,mid+1,r,rt<<1|1);
 65     return res;
 66 }
 67 
 68 struct seg
 69 {
 70     int x,y;
 71 }p[MAXN];
 72 
 73 int ask[MAXN];
 74 int b[MAXN];
 75 map <int, int> mm;
 76 
 77 int main()
 78 {
 79     int t,caseo=1;
 80     scanf("%d",&t);
 81     while(t--)
 82     {
 83         int tot=0;
 84         memset(p,0,sizeof(p));
 85         mm.clear();
 86         printf("Case #%d:\n",caseo ++);
 87         int n,m;
 88         scanf("%d%d",&n,&m);
 89         for (int i = 0; i < n; i ++)
 90         {
 91             scanf("%d%d",&p[i].x,&p[i].y);
 92             b[tot ++] = p[i].x;
 93             b[tot ++] = p[i].y;
 94         }
 95         for (int i = 0; i < m; i ++)
 96         {
 97             scanf("%d",&ask[i]);
 98             b[tot++] = ask[i];
 99         }
100         sort(b, b + tot);
101         int tt=1;
102         for (int i = 0; i < tot; i ++)
103             if (mm.find(b[i]) == mm.end())
104                 mm[b[i]] = tt ++;
105         build(tt);
106         for (int i = 0; i < n; i ++)
107             {
108                 int a = mm[p[i].x];
109                 int b = mm[p[i].y];
110                 Update(a, b, 1, 1, M, 1);
111             }
112         for (int i = 0; i < m; i ++)
113         {
114             int a = mm[ask[i]];
115             printf("%d\n",query(a, 1, M, 1));
116         }
117     }
118     return 0;
119 }

  

  ♠HDU 4267 A Simple Problem with Integers ★(2012 ACM/ICPC Asia Regional Changchun Online)(分组线段树)

  做网络赛的时候没想到(经验太少了T T……),比完赛请教了一个大牛才学会这种处理方法。

  思路: 比较容易往线段树上想的。但是由于更新的是一些离散的点,比较麻烦。可以考虑这些点的共性,总是隔几个,更新一个,而且注意这个条件“(i-a)%k==0”可以转化为“i%k == a%k ==mod ”,那么我们就可以把区间内的数关于k的余数分组。这样每次更新的都是其中的一组,而且是连续的。由于 K比较小,这是本题的突破口,那么关于k的余数情况,最多只有55种。即如果k=1,则分为1组,k=2分为2组……(如果直接开10*10的数组会MLE = =……今年卡内存卡的那叫个……)最后查询也一样,枚举点所在的所有分组上的和都加起来就行了。

HDU 4267
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <algorithm>
 11 #include <string>
 12 #include <cstring>
 13 #define MID(x,y) ( ( x + y ) >> 1 )
 14 
 15 using namespace std;
 16 
 17 #define N 50002
 18 
 19 int sum[N<<2],add[N<<2][55];
 20 int ha[11][11];
 21 int op[N];
 22 
 23 void BuildTree(int rt,int l,int r)
 24 {
 25     sum[rt]=0;
 26     memset(add[rt],0,sizeof(add[rt]));
 27     if (l==r)   return ;
 28 
 29     int mid=MID(l,r);
 30     BuildTree(rt<<1,l,mid);
 31     BuildTree(rt<<1|1,mid+1,r);
 32 }
 33 
 34 void PushDown(int rt)
 35 {
 36     if (sum[rt])
 37     {
 38         sum[rt<<1]+=sum[rt];
 39         sum[rt<<1|1]+=sum[rt];
 40         sum[rt]=0;
 41         for (int i=0;i<55;i++)
 42         {
 43             add[rt<<1][i]+=add[rt][i];
 44             add[rt<<1|1][i]+=add[rt][i];
 45             add[rt][i]=0;
 46         }
 47     }
 48 }
 49 
 50 void Update(int s,int t,int v,int l,int r,int rt,int i,int j)
 51 {
 52     if (s<=l && r<=t)
 53     {
 54         sum[rt]+=v;
 55         add[rt][ha[i][j]]+=v;
 56         return ;
 57     }
 58 
 59     PushDown(rt);
 60     int mid=MID(l,r);
 61     if (s<=mid)  Update(s,t,v,l,mid,rt<<1,i,j);
 62     if (mid<t)   Update(s,t,v,mid+1,r,rt<<1|1,i,j);
 63 }
 64 
 65 int Query(int s,int t,int l,int r,int rt)
 66 {
 67     if (s<=l && r<=t)
 68     {
 69         if (l==r)
 70         {
 71             int res=op[s];
 72             for (int i=1;i<=10;i++)  res+=add[rt][ha[i][s%i]];
 73             return res;
 74         }
 75     }
 76 
 77     PushDown(rt);
 78     int mid=MID(l,r);
 79     int res=0;
 80     if (s<=mid) res+=Query(s,t,l,mid,rt<<1);
 81     if (mid<t)  res+=Query(s,t,mid+1,r,rt<<1|1);
 82     return res;
 83 }
 84 
 85 int main()
 86 {
 87     //freopen("test.in","r+",stdin);
 88 
 89     int n;
 90     int cnt=0;
 91     for (int i=1;i<=10;i++)
 92         for (int j=0;j<i;j++)
 93             ha[i][j]=cnt++;
 94     while(~scanf("%d",&n))
 95     {
 96         BuildTree(1,1,n);
 97         memset(op,0,sizeof(op));
 98         for (int i=1;i<=n;i++)
 99             scanf("%d",&op[i]);
100         int Q;
101         scanf("%d",&Q);
102         while(Q--)
103         {
104             int p;
105             scanf("%d",&p);
106             if (p==1)
107             {
108                 int a,b,k,c;
109                 scanf("%d%d%d%d",&a,&b,&k,&c);
110                 Update(a,b,c,1,n,1,k,a%k);
111             }
112             else
113             {
114                 int a;
115                 scanf("%d",&a);
116                 printf("%d\n",Query(a,a,1,n,1));
117             }
118         }
119     }
120     return 0;
121 }

 

  ♠HDU 3954 Level up ★(2011 Alibaba Programming Contest ---by NotOnlySuccess)(分组线段树)

  HH神出的一道线段树神题~

  思路:题意很简单,成段更新,成段询问,但是更新却和一般的线段树大不一样,每个点虽然接收到相同的信息,但是由于本身不同,最终得到的值也是不同的.用一般的延迟操作就搞不定了.

  突破点在K,范围很小,只有10,可以考虑每次有人升级的时候,就递归的找下去,将这个人进行升级操作.由于找到某个人只需要logn的复杂度,每个人最多升k次,所以n个人的复杂度是O(nklogn)。用了两个辅助数组add[maxn]和MAX[maxk][maxn],add用于记录延迟标记,MAX[k]表示该区间等级为k的最大经验值.初始化add,MAX[1]为0,其他为-1,表示无人在这个等级.当MAX[k]的值大于等于Needk时,就对这个区间进行升级操作,和线段树操作一样递归的将这个区间能升级的人全部升级.

  单次操作可能会是nlogn(每个人都升级),但是平均下来还是只有nklogn.

HDU 3954
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <iomanip>
  6 #include <climits>
  7 #include <vector>
  8 #include <stack>
  9 #include <queue>
 10 #include <map>
 11 #include <algorithm>
 12 #include <string>
 13 #include <cstring>
 14 #define MID(x,y) ( ( x + y ) >> 1 )
 15 
 16 using namespace std;
 17 
 18 typedef long long LL;
 19 
 20 const int N=10005;
 21 int M;
 22 int maxn[N<<2][11],add[N<<2];
 23 int need[11];
 24 int n,k,QW;
 25 
 26 void PushUp(int rt)
 27 {
 28     for (int i=1;i<=k;i++)
 29         maxn[rt][i]=max(maxn[rt<<1][i],maxn[rt<<1|1][i]);
 30 }
 31 
 32 void PushDown(int rt)
 33 {
 34     if (add[rt])
 35     {
 36         add[rt<<1]+=add[rt];
 37         add[rt<<1|1]+=add[rt];
 38         for (int i=1;i<=k;i++)
 39         {
 40             if (maxn[rt<<1][i]!=-1)
 41                 maxn[rt<<1][i]+=i*add[rt];
 42             if (maxn[rt<<1|1][i]!=-1)
 43                 maxn[rt<<1|1][i]+=i*add[rt];
 44         }
 45         add[rt]=0;
 46     }
 47 }
 48 
 49 void BuildTree(int n)
 50 {
 51     for (M=1;M<=n+2;M<<=1);
 52 
 53     for (int i=1;i<N<<2;i++)
 54     {
 55         for (int j=0;j<11;j++)
 56             maxn[i][j]=-1;
 57         add[i]=0;
 58         maxn[i][1]=0;
 59     }
 60 }
 61 
 62 void LevelUp(int i,int s,int t,int l,int r,int rt)
 63 {
 64     if (s<=l && r<=t)
 65     {
 66         if (l==r)
 67         {
 68             while(i<k)
 69             {
 70                 if (maxn[rt][i]<need[i+1])  break;
 71                 maxn[rt][i+1]=maxn[rt][i];
 72                 maxn[rt][i]=-1;
 73                 i++;
 74             }
 75             return ;
 76         }
 77     }
 78 
 79     PushDown(rt);
 80     int mid=MID(l,r);
 81     if (s<=mid) LevelUp(i,s,t,l,mid,rt<<1);
 82     if (mid<t)  LevelUp(i,s,t,mid+1,r,rt<<1|1);
 83     PushUp(rt);
 84 }
 85 
 86 void Update(int s,int t,int ei,int l,int r,int rt)
 87 {
 88     if (s<=l && r<=t)
 89     {
 90         add[rt]+=ei;
 91         for (int i=k;i>=1;i--)
 92         {
 93             if (maxn[rt][i]!=-1)
 94             {
 95                 maxn[rt][i]+=i*ei;
 96                 if (i!=k && maxn[rt][i]>=need[i+1])
 97                     LevelUp(i,s,t,l,r,rt);
 98             }
 99         }
100 
101         return ;
102 
103     }
104 
105     PushDown(rt);
106     int mid=MID(l,r);
107     if (s<=mid) Update(s,t,ei,l,mid,rt<<1);
108     if (mid<t)  Update(s,t,ei,mid+1,r,rt<<1|1);
109     PushUp(rt);
110 }
111 
112 int Query(int s,int t,int l,int r,int rt)
113 {
114     if (s<=l && r<=t)
115     {
116         int maxnum=0;
117         for (int i=1;i<=10;i++)
118             maxnum=max(maxn[rt][i],maxnum);
119         return maxnum;
120     }
121 
122     PushDown(rt);
123     int ans=0;
124     int mid=MID(l,r);
125     if (s<=mid) ans=max(ans,Query(s,t,l,mid,rt<<1));
126     if (mid<t)  ans=max(ans,Query(s,t,mid+1,r,rt<<1|1));
127     return ans;
128 }
129 
130 int main()
131 {
132     //freopen("test.in","r+",stdin);
133 
134     int t,caseo=1;
135     scanf("%d",&t);
136     while(t--)
137     {
138         printf("Case %d:\n",caseo++);
139         scanf("%d%d%d",&n,&k,&QW);
140         BuildTree(n);
141         for (int i=2;i<=k;i++)
142             scanf("%d",&need[i]);
143         for (int i=1;i<=QW;i++)
144         {
145             char c;
146             cin>>c;
147             if (c=='W')
148             {
149                 int a,b,ei;
150                 scanf("%d%d%d",&a,&b,&ei);
151                 Update(a,b,ei,1,M,1);
152             }
153             else
154             {
155                 int a,b;
156                 scanf("%d%d",&a,&b);
157                 printf("%d\n",Query(a,b,1,M,1));
158             }
159         }
160         printf("\n");
161     }
162     return 0;
163 }

 

  ♠HDU 4358 Boring Counting ★★(2012 Multi-University Training Contest 6)

  问题抽象区间内恰好出现K次的数的个数。

  思路Here~ (内容有点儿多,单独放一处了)

 

  ♠CodeForces Round #149 E XOR on Segment ★(区间异或&&分组线段树)

  题目大意:序列a有n个数。实现两个操作:①求[l,r]区间和  ②对某个区间[l,r]所有数异或一个数x。

  思路:比较容易想到是用线段树,因为每个数都小于10^6,所以把每一位拆成20位储存,这样就用20颗线段树。那么问题就转化为区间01异或问题:0异或任何数不变,不用修改,1异或一个数x结果是1-x。。(详细理解还是看代码吧。。

View Code
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <algorithm>
#include <map>
#define MID(l,r)    (l + r)/2
using namespace std;

const int N = 100010;
long long sum[22][N<<2],cnt[N<<2],ans[22];

void pushup(int rt){
    for (int i = 0; i < 20; i ++){
        sum[i][rt] = sum[i][rt<<1] + sum[i][rt<<1|1];
    }
}
void pushdown(int rt,int w){
    if (cnt[rt]){
        cnt[rt<<1] ^= cnt[rt];
        cnt[rt<<1|1] ^= cnt[rt];
        for (int i = 0; i < 20; i ++){
            if (cnt[rt] >> i & 1){
                sum[i][rt<<1] = w - (w >> 1) - sum[i][rt<<1];
                sum[i][rt<<1|1] = (w >> 1) - sum[i][rt<<1|1];
            }
        }
        cnt[rt] = 0;
    }
}
void build(int l,int r,int rt){
    if (l == r){
        long long x;
        cin>>x;
        for (int i = 0; i < 20; i ++){
            sum[i][rt] = x >> i & 1;
        }
        return ;
    }
    int mid = MID(l,r);
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
    return ;
}

void update(int s,int t,int c,int l,int r,int rt){
    if (s <= l && r <= t){
        cnt[rt] ^= c;
        for (int i = 0; i < 20; i ++){
            if (c >> i & 1){
                sum[i][rt] = r - l + 1 - sum[i][rt];
            }
        }
        return ;
    }
    pushdown(rt,r-l+1);
    int mid = MID(l,r);
    if (s <= mid)   update(s,t,c,l,mid,rt<<1);
    if (mid < t)    update(s,t,c,mid+1,r,rt<<1|1);
    pushup(rt);
}

void query(int s,int t,int l,int r,int rt){
    if (s <= l && r <= t){
        for (int i = 0; i < 20; i ++){
            ans[i] += sum[i][rt];
        }
        return ;
    }
    pushdown(rt,r-l+1);
    int mid = MID(l,r);
    if (s <= mid)   query(s,t,l,mid,rt<<1);
    if (mid < t)    query(s,t,mid+1,r,rt<<1|1);
}

long long getquery(int s,int t,int l,int r){
    memset(ans,0,sizeof(ans));
    query(s,t,l,r,1);
    long long res = 0;
    for (int i = 0; i < 20; i ++){
        res += (1LL << i) * ans[i];
    }
    return res;
}
int main(){
    int n;
    scanf("%d",&n);
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
    build(1,n,1);
    int q;
    scanf("%d",&q);
    for (int i = 0; i < q; i ++){
        int w;
        scanf("%d",&w);
        if (w == 1){
            int a,b;
            cin>>a>>b;
            cout<<getquery(a,b,1,n)<<endl;
        }
        else{
            int a,b,c;
            cin>>a>>b>>c;
            update(a,b,c,1,n,1);
        }
    }
    return 0;
}

 

区间合并:这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并,所以一般情况下除了设置本区间最长连续mmax外,还需要设置从区间左端开始最长连续lmax和从区间右端开始最长连续rmax,以便于和左右两边区间合并。

  

  ♠POJ 3667 Hotel (区间合并入门)

  题目大意区间内最长连续房间数

  问题出来了,对于二叉树,或许某子树根的左孩子的右边跟右孩子的左边连续着呢,怎么办? 

  于是,我们开出三个数组 lsum[] rsum[] 和 sum[]。对于区间 [L, R],lsum[rt]表示以 L为开头的最长连续房间数,rsum[rt]表示以R为结尾的最长连续房间数,sum[]表示[L,R]内的最长连续房间。

  继续分析:当 lsum[rt<<1]等于左孩子区间总长度时,lsum[rt<<1]和lsum[rt<<1|1] (即左孩子的lsum和右孩子的lsum)是相连的;

  同理得, 当 rsum[rt<<1|1]等于右孩子总长度时,rsum[rt<<1|1]和rsum[rt<<1](即右孩子的rsum和左孩子的rsum)是相连的。

  而对于一个 sum[rt]= max{ rsum[rt<<1]+lsum[rt<<1|1], sum[rt<<1], sum[rt<<1|1 }。

POJ 3667
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 
 16 typedef long long LL;
 17 
 18 const int N=50005;
 19 int mmax[N<<2],lmax[N<<2],rmax[N<<2],cov[N<<2];
 20 
 21 void BuildTree(int l,int r,int rt)
 22 {
 23     mmax[rt]=lmax[rt]=rmax[rt]=r-l+1;
 24     cov[rt]=-1;
 25     if (l==r) return;
 26     int mid=MID(l,r);
 27     BuildTree(l,mid,rt<<1);
 28     BuildTree(mid+1,r,rt<<1|1);
 29 }
 30 
 31 void PushUp(int rt,int w)
 32 {
 33     lmax[rt]=lmax[rt<<1];
 34     rmax[rt]=rmax[rt<<1|1];
 35     if (lmax[rt]==w-(w>>1)) lmax[rt]+=lmax[rt<<1|1];
 36     if (rmax[rt]==(w>>1))     rmax[rt]+=rmax[rt<<1];
 37     mmax[rt]=max(rmax[rt<<1]+lmax[rt<<1|1],max(mmax[rt<<1],mmax[rt<<1|1]));
 38 }
 39 
 40 void PushDown(int rt,int w)
 41 {
 42     if (cov[rt]!=-1)        //cov的作用只是向下传递标记时区别是清空还是住满该区间。
 43     {                       //cov在区间修改时决定,而那时的Update区间意味着要么住满要么清空
 44         cov[rt<<1]=cov[rt<<1|1]=cov[rt];
 45         mmax[rt<<1]=lmax[rt<<1]=rmax[rt<<1]=cov[rt]?0:w-(w>>1); //cov=1,则mmax,lmax,rmax=0,表示住满
 46         mmax[rt<<1|1]=lmax[rt<<1|1]=rmax[rt<<1|1]=cov[rt]?0:(w>>1); //cov=0,则mmax,lmax,rmax=区间长度,表示清空
 47         cov[rt]=-1;         //一定要注意下放标记后要重置!
 48     }
 49 }
 50 
 51 void Update(int s,int t,int c,int l,int r,int rt)   //c=1表示要住进,c=0表示要清空
 52 {
 53     if (s<=l && r<=t)
 54     {
 55         cov[rt]=c;
 56         mmax[rt]=lmax[rt]=rmax[rt]=c?0:(r-l)+1;
 57         return ;
 58     }
 59 
 60     PushDown(rt,r-l+1);
 61     int mid=MID(l,r);
 62     if (s<=mid) Update(s,t,c,l,mid,rt<<1);
 63     if (mid<t)  Update(s,t,c,mid+1,r,rt<<1|1);
 64     PushUp(rt,r-l+1);
 65 }
 66 
 67 int Query(int L,int l,int r,int rt)
 68 {
 69     if (l==r)
 70         return l;
 71     PushDown(rt,r-l+1);
 72     int mid=MID(l,r);
 73     if (mmax[rt<<1]>=L)  return Query(L,l,mid,rt<<1);      //按从左向右的顺序选
 74     else if (rmax[rt<<1]+lmax[rt<<1|1]>=L) return mid-rmax[rt<<1]+1;
 75     else return Query(L,mid+1,r,rt<<1|1);
 76 }
 77 
 78 int main()
 79 {
 80     //freopen("test.in","r+",stdin);
 81     int n,m;
 82     scanf("%d%d",&n,&m);
 83     BuildTree(1,n,1);
 84     for (int i=0;i<m;i++)
 85     {
 86         int p;
 87         scanf("%d",&p);
 88         if (p==1)
 89         {
 90             int L;
 91             scanf("%d",&L);
 92             if (mmax[1]<L)  printf("%d\n",0);
 93             else
 94             {
 95                 int a=Query(L,1,n,1);
 96                 printf("%d\n",a);
 97                 Update(a,a+L-1,1,1,n,1);
 98             }
 99         }
100         else
101         {
102             int a,b;
103             scanf("%d%d",&a,&b);
104             Update(a,a+b-1,0,1,n,1);
105         }
106     }
107     return 0;
108 }

 

  ♠HDU 4339 Query (2012 Multi-University Training Contest 4)

  问题抽象最长连续公共子串

  一开始想了个二分的线段树方法,不过nlog2n超时了。。。

62 Abandon.burning 2000MS 10444K 2333B C++ 2012-10-01 08:42:19
HDU 4339
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 const int maxn = 1000100;
 16 int lmax[maxn<<2];
 17 char s[2][maxn];
 18 
 19 void pushUp(int rt,int w)
 20 {
 21     lmax[rt] = lmax[rt<<1];
 22     if (lmax[rt<<1] == w - (w >> 1))
 23         lmax[rt] += lmax[rt<<1|1];
 24 }
 25 void build(int l,int r,int rt)
 26 {
 27     if (l == r)
 28     {
 29         if (s[0][l-1] == s[1][l-1])
 30             lmax[rt] = 1;
 31         else lmax[rt] = 0;
 32         return ;
 33     }
 34     int mid = MID(l,r);
 35     build(l,mid,rt<<1);
 36     build(mid+1,r,rt<<1|1);
 37     pushUp(rt,r-l+1);
 38 }
 39 void Update(int p,int a,char c,int l,int r,int rt)
 40 {
 41     if (p == l && p == r)
 42     {
 43         s[a-1][p-1] = c;
 44         if (s[0][p-1] == s[1][p-1])
 45             lmax[rt] = 1;
 46         else lmax[rt] = 0;
 47         return ;
 48     }
 49 
 50     int mid = MID(l,r);
 51     if (p <= mid)   Update(p,a,c,l,mid,rt<<1);
 52     else Update(p,a,c,mid+1,r,rt<<1|1);
 53     pushUp(rt,r-l+1);
 54 }
 55 
 56 int query(int s,int t,int l,int r,int rt)
 57 {
 58     if (s <= l && r <= t)
 59     {
 60         return lmax[rt];
 61     }
 62 
 63     int mid = MID(l,r);
 64     int res = 0;
 65     if (s <= mid)
 66     {
 67         res += query(s,t,l,mid,rt<<1);
 68         if (res != mid - s + 1) return res;
 69     }
 70     if (mid < t)    res += query(s,t,mid+1,r,rt<<1|1);
 71     return res;
 72 }
 73 
 74 int main()
 75 {
 76     //freopen("test.in","r+",stdin);
 77 
 78     int t, caseo = 1;
 79     scanf("%d", &t);
 80     while(t--)
 81     {
 82         printf("Case %d:\n", caseo ++);
 83         scanf("%s",s[0]);
 84         scanf("%s",s[1]);
 85         int n = max(strlen(s[0]), strlen(s[1]));
 86         build(1,n,1);
 87         int Q;
 88         scanf("%d",&Q);
 89         while(Q--)
 90         {
 91             int d;
 92             scanf("%d",&d);
 93             if (d == 1)
 94             {
 95                 int a,p;
 96                 char c;
 97                 scanf("%d%d",&a,&p);
 98                 cin>>c;
 99                 p ++;
100                 Update(p,a,c,1,n,1);
101             }
102             else
103             {
104                 int p;
105                 scanf("%d",&p);
106                 p ++;
107                 printf("%d\n",query(p,n,1,n,1));
108             }
109         }
110     }
111     return 0;
112 }

 

  ♠HDU 4351 Digital root ★★(2012 Multi-University Training Contest 6) (线段树值得研究的一道好题)

  区间合并->二次合并(pushup)->更新时要合并左右子区间,询问时要合并左右询问子区间(这是不一样的,比如在[1,8]中询问[1,6],那么左右子区间分别是[1,4],[5,8],而左右询问子区间是[1,4],[5,6])。

  预备知识一个数的数字根就是这个数mod 9的余数 (余数为0则取9,当然如果这个数本身是0那就是0了……)

  思路:因为题目中所查询的区间,是所有的子区间的结果的并。所以区间合并就很必要了。

  每个结点,记录这个区间的和的数根, 记录本区间内从左端点为端点往右的连续区间出现的数根,从右端点为端点往左的连续区间出现的数根,以及整个区间能出现的数根。然后合并什么的。。。

  但显然还没有结束,不然我也不会把他视作好题了。。。这道题用线段树会卡时限!需要各种优化------主要是算数字根的打表优化。

  做了一天吧T_T。。。上午想好了思路,然后一下午+晚上完成了一个加上打表、输入输出优化还是TLE的代码。。。T_T。。。然后就捉鸡了。。。

HDU 4351(TLE sb代码)
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 const int maxn = 100100;
 16 int mmax[maxn<<2],lsum[maxn<<2][10],rsum[maxn<<2][10],ssum[maxn<<2][10];
 17 int mans[maxn<<2],ans[maxn<<2][10],lans[maxn<<2][10],rans[maxn<<2][10];
 18 int mul[10][10];    //合并i,j打表优化
 19 
 20 void initial()
 21 {
 22     for (int i = 0; i < 10; i ++)
 23         for (int j = 0; j < 10; j ++)
 24         {
 25             int k = i + j;
 26             while (k > 9)
 27                 k -= 9;
 28             mul[i][j] = k;
 29         }
 30 }
 31 //外挂优化。。。
 32 char CHAR()
 33 {
 34      char res;
 35      while (res = getchar(), !isalpha(res));
 36      return res;
 37 }
 38 inline void scanf_(int &num)
 39 {
 40     char in;
 41     bool neg = false;
 42     while(((in = getchar()) > '9' || in <'0') && in != '-') ;
 43     if(in == '-')
 44     {
 45         neg = true;
 46         while((in = getchar()) >'9' || in < '0');
 47     }
 48     num = in - '0';
 49     while(in = getchar(),in >= '0' && in <= '9')
 50         num *= 10, num += in - '0';
 51     if(neg)
 52         num = 0 - num;
 53 }
 54 inline void printf_(int num)
 55 {
 56     if(num < 0)
 57     {
 58         putchar('-');
 59         num =- num;
 60     }
 61     int ans[10],top = 0;
 62     while(num != 0)
 63     {
 64         ans[top ++] = num % 10;
 65         num /= 10;
 66     }
 67     if(top == 0)
 68         putchar('0');
 69     for(int i = top - 1; i >= 0; i --)
 70     {
 71         char ch = ans[i] + '0';
 72         putchar(ch);
 73     }
 74     //putchar('\n');
 75 }
 76 
 77 //线段树部分
 78 inline void pushUp(int rt)
 79 {
 80     for (int i = 0; i < 10; i ++)
 81     {
 82         lsum[rt][i] = lsum[rt<<1][i];
 83         rsum[rt][i] = rsum[rt<<1|1][i];
 84         ssum[rt][i] = (ssum[rt<<1][i] == 1)?1:ssum[rt<<1|1][i];
 85     }
 86     //修改区间和数字根
 87     int k = mul[mmax[rt<<1]][mmax[rt<<1|1]];
 88     mmax[rt] = k,  ssum[rt][k] = 1;
 89 
 90     for (int i = 0; i < 10; i ++)
 91     {
 92         //右前缀+区间和扩展前缀数字根和子区间数字根
 93         if (lsum[rt<<1|1][i])
 94         {
 95             int k = mul[mmax[rt<<1]][i];
 96             lsum[rt][k] = 1,   ssum[rt][k] = 1;
 97         }
 98         //左后缀+区间和扩展后缀数字根和子区间数字根
 99         if (rsum[rt<<1][i])
100         {
101             int k = mul[mmax[rt<<1|1]][i];
102             rsum[rt][k] = 1,   ssum[rt][k] = 1;
103 
104             //左后缀+右前缀扩展子区间数字根
105             for (int j = 0; j < 10; j ++)
106                 if (lsum[rt<<1|1][j])
107                 {
108                     int k = mul[i][j];
109                     ssum[rt][k] = 1;
110                 }
111         }
112     }
113 }
114 
115 inline void pushUpAns(int rt)
116 {
117     //查询时也要合并左右区间...
118     for (int i = 0; i < 10; i ++)
119     {
120         lans[rt][i] = lans[rt<<1][i];
121         rans[rt][i] = rans[rt<<1|1][i];
122         ans[rt][i] = (ans[rt<<1][i] == 1)?1:ans[rt<<1|1][i];
123     }
124     for (int i = 0; i < 10; i ++)
125     {
126         if (lans[rt<<1|1][i])
127         {
128             int k = mul[mans[rt<<1]][i];
129             lans[rt][k] = 1,   ans[rt][k] = 1;
130         }
131         if (rans[rt<<1][i])
132         {
133             int k = mul[mans[rt<<1|1]][i];
134             rans[rt][k] = 1,   ans[rt][k] = 1;
135         }
136         if (rans[rt<<1][i])
137             for (int j = 0; j < 10; j ++)
138                 if (lans[rt<<1|1][j])
139                 {
140                     int k = mul[i][j];
141                     ans[rt][k] = 1;
142                 }
143     }
144 }
145 
146 void build(int l,int r,int rt)
147 {
148     if (l == r)
149     {
150         mmax[rt] = 0;
151         for (int i = 0; i < 10; i ++)
152         {
153             lsum[rt][i] = 0;
154             rsum[rt][i] = 0;
155             ssum[rt][i] = 0;
156         }
157         int a;
158         scanf_(a);
159         if (!a) {   mmax[rt] = 0; lsum[rt][0] = 1;   rsum[rt][0] = 1;   ssum[rt][0] = 1;   }
160         else
161         {
162             a = a % 9;
163             if (!a) {   mmax[rt] = 9; lsum[rt][9] = 1;   rsum[rt][9] = 1;   ssum[rt][9] = 1;  }
164             else {  mmax[rt] = a; lsum[rt][a] = 1;   rsum[rt][a] = 1;    ssum[rt][a] = 1; }
165         }
166         return ;
167     }
168     int mid = MID(l,r);
169     build(l, mid, rt<<1);
170     build(mid+1, r, rt<<1|1);
171     pushUp(rt);
172 }
173 
174 int query(int s,int t,int l,int r,int rt)
175 {
176     if (s <= l && r <= t)
177     {
178         for (int i = 9; i >= 0; i --)
179         {
180             if (ssum[rt][i])
181                 ans[rt][i] = 1;
182             if (lsum[rt][i])
183                 lans[rt][i] = 1;
184             if (rsum[rt][i])
185                 rans[rt][i] = 1;
186         }
187         mans[rt] = mmax[rt];
188         while (mans[rt] > 9)   mans[rt] -= 9;
189         return mmax[rt];
190     }
191     int res = 0;
192     int mid = MID(l,r);
193     if (s <= mid)   res += query(s,t,l,mid,rt<<1);
194     if (mid < t)    res += query(s,t,mid+1,r,rt<<1|1);
195     mans[rt] = res;
196     while (mans[rt] > 9)   mans[rt] -= 9;
197     pushUpAns(rt);
198     return res;
199 }
200 
201 int main()
202 {
203     //freopen("test.in","r+",stdin);
204     initial();
205     int t, caseo = 1;
206     scanf_(t);
207     while(t--)
208     {
209         printf("Case #%d:\n",caseo ++);
210         int n;
211         scanf_(n);
212         build(1,n,1);
213         int Q;
214         scanf_(Q);
215         while(Q--)
216         {
217             memset(ans,0,sizeof(ans));
218             memset(lans,0,sizeof(lans));
219             memset(rans,0,sizeof(lans));
220             memset(mans,0,sizeof(mans));
221             int l,r;
222             scanf_(l),scanf_(r);
223             query(l,r,1,n,1);
224             int tot = 0;
225             for (int i = 9; i >= 0; i --)
226                 if (ans[1][i] && tot < 5)
227                 {
228                     tot ++;
229                   //tot == 5?printf("%d\n",i):printf("%d ",i);
230                     if (tot == 5)
231                         printf_(i),putchar('\n');
232                     else    printf_(i),putchar(' ');
233                 }
234             while(tot < 5)
235             {
236                 tot ++;
237                 //tot == 5?printf("-1\n"):printf("-1 ");
238                 if (tot == 5)
239                     puts("-1");
240                 else
241                 {
242                     putchar('-');
243                     putchar('1');
244                     putchar(' ');
245                 }
246             }
247         }
248         if (t)  printf("\n");
249     }
250     return 0;
251 }

  噗。。。然后第二天上午终于找到哪儿超时了(我说怎么加了打表优化和输入输出优化还超T_T),竟然是开始回答询问时的那四个memset T_T,然后恍然大悟。。。我竟然sb的给ans也建了颗线段树!活该作死啊。。。T_T。。。

  然后把ans改为节点,加上打表优化、输入输出外挂后终于A了T_T  (速度还不是很快啊。。。网上找了个程序1100MS呜呜。。。):

6851969 2012-10-02 15:18:54 Accepted 4351 1546MS 32140K 6320 B G++ Abandon.burning
HDU 4351
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 const int maxn = 100100;
 16 int mmax[maxn<<2],lsum[maxn<<2][10],rsum[maxn<<2][10],ssum[maxn<<2][10];
 17 int mul[10][10];    //合并i,j打表优化
 18 
 19 struct ANS
 20 {
 21     int ls[10], rs[10];
 22     int res[10];
 23     int all;
 24     ANS()
 25     {
 26         memset(ls,0,sizeof(ls));
 27         memset(rs,0,sizeof(rs));
 28         memset(res,0,sizeof(res));
 29         all = 0;
 30     }
 31 };
 32 
 33 void initial()
 34 {
 35     for (int i = 0; i < 10; i ++)
 36         for (int j = 0; j < 10; j ++)
 37         {
 38             int k = i + j;
 39             while (k > 9)
 40                 k -= 9;
 41             mul[i][j] = k;
 42         }
 43 }
 44 //外挂优化。。。
 45 inline void scanf_(int &num)
 46 {
 47     char in;
 48     bool neg = false;
 49     while(((in = getchar()) > '9' || in <'0') && in != '-') ;
 50     if(in == '-')
 51     {
 52         neg = true;
 53         while((in = getchar()) >'9' || in < '0');
 54     }
 55     num = in - '0';
 56     while(in = getchar(),in >= '0' && in <= '9')
 57         num *= 10, num += in - '0';
 58     if(neg)
 59         num = 0 - num;
 60 }
 61 inline void printf_(int num)
 62 {
 63     if(num < 0)
 64     {
 65         putchar('-');
 66         num =- num;
 67     }
 68     int ans[10],top = 0;
 69     while(num != 0)
 70     {
 71         ans[top ++] = num % 10;
 72         num /= 10;
 73     }
 74     if(top == 0)
 75         putchar('0');
 76     for(int i = top - 1; i >= 0; i --)
 77     {
 78         char ch = ans[i] + '0';
 79         putchar(ch);
 80     }
 81     //putchar('\n');
 82 }
 83 
 84 //线段树部分
 85 inline void pushUp(int rt)
 86 {
 87     for (int i = 0; i < 10; i ++)
 88     {
 89         lsum[rt][i] = lsum[rt<<1][i];
 90         rsum[rt][i] = rsum[rt<<1|1][i];
 91         ssum[rt][i] = (ssum[rt<<1][i] == 1)?1:ssum[rt<<1|1][i];
 92     }
 93     //修改区间和数字根
 94     int k = mul[mmax[rt<<1]][mmax[rt<<1|1]];
 95     mmax[rt] = k,  ssum[rt][k] = 1;
 96 
 97     for (int i = 0; i < 10; i ++)
 98     {
 99         //右前缀+区间和扩展前缀数字根和子区间数字根
100         if (lsum[rt<<1|1][i])
101         {
102             int k = mul[mmax[rt<<1]][i];
103             lsum[rt][k] = 1,   ssum[rt][k] = 1;
104         }
105         //左后缀+区间和扩展后缀数字根和子区间数字根
106         if (rsum[rt<<1][i])
107         {
108             int k = mul[mmax[rt<<1|1]][i];
109             rsum[rt][k] = 1,   ssum[rt][k] = 1;
110 
111             //左后缀+右前缀扩展子区间数字根
112             for (int j = 0; j < 10; j ++)
113                 if (lsum[rt<<1|1][j])
114                 {
115                     int k = mul[i][j];
116                     ssum[rt][k] = 1;
117                 }
118         }
119     }
120 }
121 
122 inline void pushUpAns(int rt,ANS &p,ANS &lp,ANS &rp)
123 {
124     //查询时也要合并左右区间...
125     for (int i = 0; i < 10; i ++)
126     {
127         p.ls[i] = lp.ls[i];
128         p.rs[i] = rp.rs[i];
129         p.res[i] = (lp.res[i] == 1)?1:rp.res[i];
130     }
131     for (int i = 0; i < 10; i ++)
132     {
133         if (rp.ls[i])
134         {
135             int k = mul[lp.all][i];
136             p.ls[k] = 1,   p.res[k] = 1;
137         }
138         if (lp.rs[i])
139         {
140             int k = mul[rp.all][i];
141             p.rs[k] = 1,   p.res[k] = 1;
142         }
143         if (lp.rs[i])
144             for (int j = 0; j < 10; j ++)
145                 if (rp.ls[j])
146                 {
147                     int k = mul[i][j];
148                     p.res[k] = 1;
149                 }
150     }
151 }
152 
153 void build(int l,int r,int rt)
154 {
155     if (l == r)
156     {
157         mmax[rt] = 0;
158         for (int i = 0; i < 10; i ++)
159         {
160             lsum[rt][i] = 0;
161             rsum[rt][i] = 0;
162             ssum[rt][i] = 0;
163         }
164         int a;
165         scanf_(a);
166         //scanf("%d",&a);
167         if (!a) {   mmax[rt] = 0; lsum[rt][0] = 1;   rsum[rt][0] = 1;   ssum[rt][0] = 1;   }
168         else
169         {
170             a = a % 9;
171             if (!a) {   mmax[rt] = 9; lsum[rt][9] = 1;   rsum[rt][9] = 1;   ssum[rt][9] = 1;  }
172             else {  mmax[rt] = a; lsum[rt][a] = 1;   rsum[rt][a] = 1;    ssum[rt][a] = 1; }
173         }
174         return ;
175     }
176     int mid = MID(l,r);
177     build(l, mid, rt<<1);
178     build(mid+1, r, rt<<1|1);
179     pushUp(rt);
180 }
181 
182 int query(int s,int t,int l,int r,int rt,ANS &p)
183 {
184     if (s <= l && r <= t)
185     {
186         for (int i = 9; i >= 0; i --)
187         {
188             if (ssum[rt][i])
189                 p.res[i] = 1;
190             if (lsum[rt][i])
191                 p.ls[i] = 1;
192             if (rsum[rt][i])
193                 p.rs[i] = 1;
194         }
195         p.all = mmax[rt];
196         while (p.all > 9)   p.all -= 9;
197         return p.all;
198     }
199     int res = 0;
200     int mid = MID(l,r);
201     ANS lp,rp;
202     if (s <= mid)   res += query(s,t,l,mid,rt<<1,lp);
203     if (mid < t)    res += query(s,t,mid+1,r,rt<<1|1,rp);
204     p.all = res;
205     while (p.all > 9)   p.all -= 9;
206     pushUpAns(rt,p,lp,rp);
207     return res;
208 }
209 
210 int main()
211 {
212 //    freopen("data.txt","r+",stdin);
213 //    freopen("ans.txt","w+",stdout);
214     initial();
215     int t, caseo = 1;
216     scanf_(t);
217     //scanf("%d",&t);
218     while(t--)
219     {
220         printf("Case #%d:\n",caseo ++);
221         int n;
222         scanf_(n);
223         //scanf("%d",&n);
224         build(1,n,1);
225         int Q;
226         scanf_(Q);
227         //scanf("%d",&Q);
228         while(Q--)
229         {
230             ANS ans;
231             int l,r;
232             scanf_(l),scanf_(r);
233             //scanf("%d%d",&l,&r);
234             query(l,r,1,n,1,ans);
235             int tot = 0;
236             for (int i = 9; i >= 0; i --)
237                 if (ans.res[i] && tot < 5)
238                 {
239                     tot ++;
240                     //tot == 5?printf("%d\n",i):printf("%d ",i);
241                     if (tot == 5)
242                         printf_(i),putchar('\n');
243                     else    printf_(i),putchar(' ');
244                 }
245             while(tot < 5)
246             {
247                 tot ++;
248                 //tot == 5?printf("-1\n"):printf("-1 ");
249                 if (tot == 5)
250                     puts("-1");
251                 else
252                 {
253                     putchar('-');
254                     putchar('1');
255                     putchar(' ');
256                 }
257             }
258         }
259         if (t)  printf("\n");
260     }
261     return 0;
262 }

 

扫描线:这类题目需要将一些操作排序,然后从左到右用一根扫描线(当然是在我们脑子里)扫过去。最典型的就是矩形面积并,周长并等题

 

  ♠HDU 1542 Atlantis (矩形面积并入门题)

  问题抽象矩形面积并

  其他入门题:POJ 1151 (就是这道题,不过POJ的数据比较弱...)、POJ 1389POJ 3277(所有矩形底边都在一条水平线上)

  思路:浮点数先要离散化; 对于一个矩形(x1, y1, x2, y2),只取上边和下边,结构体ss[] 记录一条边的左右点坐标和距坐标轴的高度,再拿一个变量 ss[].s记录这条边是上边还是下边,下边记录为1,上边记录为-1。按高度排序,对横轴建树,从低到高扫描一遍。若下边,则加入线段树;若上边,则从线段树上去掉。用cnt表示该区间下边比上边多几条线段,sum代表该区间内被覆盖的长度总和。

  这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方要好好的琢磨一下。

  可以去看看nocLyt的图解:http://blog.sina.com.cn/s/blog_5e8c2b4e01011j9e.html

HDU 1542
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 const int maxn = 500;
 16 double X[maxn];
 17 double sum[maxn<<2];
 18 int cnt[maxn<<2];
 19 
 20 struct Seg
 21 {
 22     double l,r,h;
 23     int s;
 24     Seg(){}
 25     Seg(double a,double b,double c,int d):l(a),r(b),h(c),s(d){}
 26     bool operator < (const Seg &cmp) const
 27     {
 28         return h < cmp.h;
 29     }
 30 }ss[maxn];
 31 
 32 void pushup(int rt,int l,int r)
 33 {
 34     if (cnt[rt])    sum[rt] = X[r+1] - X[l];
 35     else if (l == r)    sum[rt] = 0;
 36     else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 37 }
 38 void update(int s,int t,int v,int l,int r,int rt)
 39 {
 40     if (s <= l && r <= t)
 41     {
 42         cnt[rt] += v;
 43         pushup(rt,l,r);
 44         return ;
 45     }
 46     int mid = MID(l,r);
 47     if (s <= mid)   update(s,t,v,l,mid,rt<<1);
 48     if (mid < t)    update(s,t,v,mid+1,r,rt<<1|1);
 49     pushup(rt,l,r);
 50 }
 51 int binfind(double x,int n,double X[])
 52 {
 53     int head = 0, tail = n - 1;
 54     while(head <= tail)
 55     {
 56         int mid = MID(head,tail);
 57         if (X[mid] == x)    return mid;
 58         if (X[mid] < x)     head = mid + 1;
 59         else    tail = mid - 1;
 60     }
 61     return -1;
 62 }
 63 int main()
 64 {
 65     //freopen("data.txt","r+",stdin);
 66 
 67     int cas = 1;
 68     int n;
 69     while(~scanf("%d",&n) && n)
 70     {
 71         int tot = 0;
 72         while(n--)
 73         {
 74             double a,b,c,d;
 75             scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
 76             X[tot] = a;
 77             ss[tot ++] = Seg(a,c,b,1);
 78             X[tot] = c;
 79             ss[tot ++] = Seg(a,c,d,-1);
 80         }
 81         sort(X,X+tot);
 82         sort(ss,ss+tot);
 83         int ptot = 1;
 84         for (int i = 1; i < tot ; i ++) //зјБъШЅжи
 85             if (X[i]!=X[i-1])   X[ptot ++] = X[i];
 86 
 87         //build(1,ptot,1);
 88         memset(cnt , 0 , sizeof(cnt));
 89         memset(sum , 0 , sizeof(sum));
 90         double res = 0;
 91         for (int i = 0; i < tot - 1; i ++)
 92         {
 93             int l = binfind(ss[i].l, ptot, X);
 94             int r = binfind(ss[i].r, ptot, X) - 1;
 95             if (l <= r) update(l, r, ss[i].s, 0, ptot-1 , 1);
 96             res += sum[1] * (ss[i+1].h - ss[i].h);
 97         }
 98         printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , res);
 99     }
100 
101     return 0;
102 }

 

  ♠HDU 3265 Posters (2009 ACM/ICPC Asia Ningbo Regional Contest)

  问题抽象矩形面积并

  贴矩形海报,每次贴前都要在海报中剪个小的矩形洞,然后求面积并。思路很简单,把单个海报剪掉一个洞后看成四个小海报就行了,分成四个小矩形时要注意下边界处理问题,然后直接套HDU 1542的模板1Y了 =.=。。。

HDU 3265
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 const int maxn = 400100;
 16 int X[maxn];
 17 long long sum[maxn<<2];
 18 int cnt[maxn<<2];
 19 
 20 struct Seg
 21 {
 22     int l,r,h;
 23     int s;
 24     Seg(){}
 25     Seg(int a,int b,int c,int d):l(a),r(b),h(c),s(d){}
 26     bool operator < (const Seg &cmp) const
 27     {
 28         return h < cmp.h;
 29     }
 30 }ss[maxn];
 31 
 32 void pushup(int rt,int l,int r)
 33 {
 34     if (cnt[rt])    sum[rt] = X[r+1] - X[l];
 35     else if (l == r)    sum[rt] = 0;
 36     else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
 37 }
 38 void update(int s,int t,int v,int l,int r,int rt)
 39 {
 40     if (s <= l && r <= t)
 41     {
 42         cnt[rt] += v;
 43         pushup(rt,l,r);
 44         return ;
 45     }
 46     int mid = MID(l,r);
 47     if (s <= mid)   update(s,t,v,l,mid,rt<<1);
 48     if (mid < t)    update(s,t,v,mid+1,r,rt<<1|1);
 49     pushup(rt,l,r);
 50 }
 51 int binfind(int x,int n,int X[])
 52 {
 53     int head = 0, tail = n - 1;
 54     while(head <= tail)
 55     {
 56         int mid = MID(head,tail);
 57         if (X[mid] == x)    return mid;
 58         if (X[mid] < x)     head = mid + 1;
 59         else    tail = mid - 1;
 60     }
 61     return -1;
 62 }
 63 int main()
 64 {
 65     //freopen("data.txt","r+",stdin);
 66     int n;
 67     while(~scanf("%d",&n) && n)
 68     {
 69         int tot = 0;
 70         while(n--)
 71         {
 72             int x1,y1,x2,y2,x3,y3,x4,y4;
 73             scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
 74             if (x1 < x3)
 75             {
 76                 X[tot] = x1;
 77                 ss[tot++] = Seg(x1,x3,y3,1);
 78                 X[tot] = x3;
 79                 ss[tot++] = Seg(x1,x3,y4,-1);
 80             }
 81             if (x4 < x2)
 82             {
 83                 X[tot] = x4;
 84                 ss[tot++] = Seg(x4,x2,y3,1);
 85                 X[tot] = x2;
 86                 ss[tot++] = Seg(x4,x2,y4,-1);
 87             }
 88             if (y4 < y2)
 89             {
 90                 X[tot] = x1;
 91                 ss[tot++] = Seg(x1,x2,y4,1);
 92                 X[tot] = x2;
 93                 ss[tot++] = Seg(x1,x2,y2,-1);
 94             }
 95             if (y1 < y3)
 96             {
 97                 X[tot] = x1;
 98                 ss[tot++] = Seg(x1,x2,y1,1);
 99                 X[tot] = x2;
100                 ss[tot++] = Seg(x1,x2,y3,-1);
101             }
102         }
103         sort(X,X+tot);
104         sort(ss,ss+tot);
105         int ptot = 1;
106         for (int i = 1; i < tot ; i ++)
107             if (X[i]!=X[i-1])   X[ptot ++] = X[i];
108 
109         memset(cnt , 0 , sizeof(cnt));
110         memset(sum , 0 , sizeof(sum));
111         long long res = 0;
112         for (int i = 0; i < tot - 1; i ++)
113         {
114             int l = binfind(ss[i].l, ptot, X);
115             int r = binfind(ss[i].r, ptot, X) - 1;
116             if (l <= r) update(l, r, ss[i].s, 0, ptot-1 , 1);
117             res += sum[1] * (ss[i+1].h - ss[i].h);
118         }
119         printf("%I64d\n",res);
120     }
121 
122     return 0;
123 }

 

二维线段树:此类线段树没什么意思,就是代码量加倍了而已,运用范围也没一维线段树广,会写就行了

  二维线段树需要有两个维度,所以实现它的最基本思想就是树中套树。假设有一个矩形横坐标范围1—n,纵坐标范围1—m。我们可以以横坐标为一个维度,建立一棵线段树,假设为tree1,在这棵树的每个节点中以纵坐标建立一棵线段树,设为tree2,假设我们在tree1所处在的节点的的横坐标范围为l,r,那么该节点表示的矩形范围为横坐标为l—r,纵坐标范围为1—m。若我们正处在该节点中tree2的某个节点,该节点的纵坐标范围为d—u,那么tree2中的这个节点所代表的矩形范围,横坐标l—r,纵坐标d—u。所以我们看到,树套树&&二维线段树只要理解其思想就会发现其实并不难。

 

  ♠HDU 1823 Luck and Love (二维线段树入门)

  因为有两个限制范围,所以需要二维的线段树。也没什么好说的地方。

  需要注意几个问题:1.初始化sum = -1,不然所有的返回结果都是>=0的数,查找不到的情况不好判断。

           2.坐标左小右大(因为这个白白贡献几次WA擦。。。)

           3.有可能存在同一身高、活泼度的人,所以更新时不是直接赋值而是选个缘分值最大的。

HDU 1823
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <vector>
  6 #include <stack>
  7 #include <queue>
  8 #include <map>
  9 #include <algorithm>
 10 #include <string>
 11 #include <cstring>
 12 #define MID(x,y) ((x+y)>>1)
 13 
 14 using namespace std;
 15 
 16 const int maxn1 = 105;
 17 const int maxn2 = 1050;
 18 int sum[maxn1<<2][maxn2<<2];
 19 int n1 = 100, n2 = 1000;
 20 
 21 void build()
 22 {
 23     memset(sum,-1,sizeof(sum));
 24 }
 25 void update2(int p,int c,int l,int r,int rt1,int rt2)
 26 {
 27     if (l == p && r == p)
 28     {
 29         sum[rt1][rt2] = max(sum[rt1][rt2], c);      //重要!:有可能存在身高、活泼度一样的美女,要选缘分值高的
 30         return ;
 31     }
 32 
 33     int mid = MID(l,r);
 34     if (p <= mid)   update2(p,c,l,mid,rt1,rt2<<1);
 35     else    update2(p,c,mid+1,r,rt1,rt2<<1|1);
 36     sum[rt1][rt2] = max(sum[rt1][rt2<<1],sum[rt1][rt2<<1|1]);
 37 }
 38 void update1(int h,int p,int c,int l,int r,int rt)  //h身高,p活泼度
 39 {
 40     update2(p,c,0,n2,rt,1);
 41     if (l == h && r == h)
 42         return ;
 43     int mid = MID(l,r);
 44     if (h <= mid)   update1(h,p,c,l,mid,rt<<1);
 45     else    update1(h,p,c,mid+1,r,rt<<1|1);
 46 }
 47 int query2(int p1,int p2,int l,int r,int rt1,int rt2)
 48 {
 49     if (p1 <= l && r <= p2)
 50     {
 51         return sum[rt1][rt2];
 52     }
 53     int mid = MID(l,r);
 54     int res = -1;
 55     if (p1 <= mid)   res = max(res, query2(p1,p2,l,mid,rt1,rt2<<1));
 56     if (mid < p2)    res = max(res, query2(p1,p2,mid+1,r,rt1,rt2<<1|1));
 57     return res;
 58 }
 59 int query1(int h1,int h2,int p1,int p2,int l,int r,int rt)
 60 {
 61     if (h1 <= l && r <= h2)
 62         return query2(p1,p2,0,n2,rt,1);
 63     int mid = MID(l,r);
 64     int res = -1;
 65     if (h1 <= mid)  res = max(res, query1(h1,h2,p1,p2,l,mid,rt<<1));
 66     if (mid < h2)   res = max(res, query1(h1,h2,p1,p2,mid+1,r,rt<<1|1));
 67     return res;
 68 }
 69 int main()
 70 {
 71     //freopen("data.txt","r+",stdin);
 72 
 73     int m;
 74     while(scanf("%d",&m),m)
 75     {
 76         build();
 77         while(m--)
 78         {
 79             char s[2];
 80             scanf("%s",s);
 81             if (s[0] == 'I')
 82             {
 83                 int h;
 84                 double p,num;
 85                 scanf("%d%lf%lf",&h,&p,&num);
 86                 update1(h,int(p*10),int(num*10),n1,n1+100,1);
 87             }
 88             else
 89             {
 90                 int h1,h2;
 91                 double p1,p2;
 92                 scanf("%d%d%lf%lf",&h1,&h2,&p1,&p2);
 93                 if (h1 > h2)    swap(h1,h2);
 94                 if (p1 > p2)    swap(p1,p2);
 95                 int res = query1(h1,h2,int(p1*10),int(p2*10),n1,n1+100,1);
 96                 if (res < 0)    printf("-1\n");
 97                 else printf("%.1lf\n",res*1.0/10.0);
 98             }
 99         }
100     }
101 
102     return 0;
103 }

 

(未完待续...)

posted @ 2012-09-15 22:24  AbandonZHANG  阅读(1406)  评论(0编辑  收藏  举报