1. 程式人生 > >線段樹(單點修改,區間求和,區間最大)

線段樹(單點修改,區間求和,區間最大)

(一)線段樹

1.E - Lost Cows

 

N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands. 

Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow. 

Given this data, tell FJ the exact ordering of the cows. 

Input

* Line 1: A single integer, N 

* Lines 2..N: These N-1 lines describe the number of cows that precede a given cow in line and have brands smaller than that cow. Of course, no cows precede the first cow in line, so she is not listed. Line 2 of the input describes the number of preceding cows whose brands are smaller than the cow in slot #2; line 3 describes the number of preceding cows whose brands are smaller than the cow in slot #3; and so on. 

Output

* Lines 1..N: Each of the N lines of output tells the brand of a cow in line. Line #1 of the output tells the brand of the first cow in line; line 2 tells the brand of the second cow; and so on.

Sample Input

5
1
2
1
0

Sample Output

2
4
5
3
1
解題思路:題意是已知每個數字前面比她小的數的個數組成pre[]陣列,讓我們求他們的ans[]順序。
我們可以用暴力的方法來解決這道題:
思路是從後往前處理pre陣列,例如pre[]:0 1 2 1 0
pre[5]=0,表示ans[5]前面比她小的有0個,即他是最小的,因此ans[5]=1;同時ans陣列中將不再包括1
pre[4]=1,表示ans[4]前面比他小的有1個,即他是第二小,因此ans[4]=3;同時ans陣列不在包括3
。。。
每次都是從剩下的編號裡找第pre[i]+1小,就是ans[i]
我們解題用到三個陣列,pre,ans,num[]陣列記錄被處理過的數字,處理過就記為-1,設定num的初始值就為下標值
程式碼:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=8010;

int pre[N],ans[N],num[N];//陣列的第0個都不用,從1開始

int main()
{
    int n,i,j;
    scanf("%d",&n);
    pre[1]=0;
    for(i=2;i<=n;i++)
        scanf("%d",&pre[i]);
    for(i=1;i<=n;i++)
        num[i]=i;//給num賦初值
    for(i=n;i>=1;i--)
    {
        int k=0;
        for(j=1;j<=n;j++)
        {
            if(num[j]!=-1)
            {
                k++;
                if(k==pre[i]+1)//找到第pre[i]+1小的數
                {
                    ans[i]=num[j];//用ans記錄
                    num[j]=-1;//num陣列標記
                    break;
                }
            }
        }
    }
    for(i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

用暴力如果數量級較大就不太適合了會TLE,那麼就引出我們的線段樹

我們用線斷樹實現的是建樹與修改和查詢。

程式碼:

#include<iostream>
#include<cstdio>
using namespace std;
const int  N=10000;
struct{
    int l,r,len;//len儲存這個區間的數字個數
}tree[4*N];
int pre[N],ans[N];
void BuildTree(int left,int right,int u)//建樹
{
    tree[u].l=left;
    tree[u].r=right;
    tree[u].len=right-left+1;
    if(left==right)
    {
        return;
    }
    BuildTree(left,(left+right)>>1,u<<1);//遞迴左子樹
    BuildTree(((left+right)>>1)+1,right,(u<<1)+1);//遞迴右子樹
}
int query(int u,int num)//維護+查詢
{
    tree[u].len--;//該區間長度-1
    if(tree[u].l==tree[u].r)
        return tree[u].l;//找到該點返回他的下標
    if(tree[u<<1].len<num)//如果左子樹的各數不夠,就查詢右子樹起第num-tree[u<<1].len的數下標
        return query((u<<1)+1,num-tree[u<<1].len);
    if(tree[u<<1].len>=num)//左子樹個數足夠,就找第num個數
        return query(u<<1,num);
}
int main()
{
    int n,i;
    scanf("%d",&n);
    pre[1]=0;
    for(i=2;i<=n;i++)
        scanf("%d",&pre[i]);
    BuildTree(1,n,1);//先建樹
    for(i=n;i>=1;i--)//從後往前找
        ans[i]=query(1,pre[i]+1);
    for(i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

2.G - A Simple Problem with Integers

You have N integers, A1A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15
解題思路:區間修改與求和(模板題)
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+10;
long long sum[N<<2],add[N<<2];
void push_up(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
//更新rt的子節點
void push_down(int rt,int m)
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        sum[rt<<1]+=(m-(m>>1))*add[rt];
        sum[rt<<1|1]+=(m>>1)*add[rt];
        add[rt]=0;//取消標記
    }
}
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void build(int l,int r,int rt)
{
    add[rt]=0;
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int mid=(r+l)>>1;
    build(lson);
    build(rson);
    push_up(rt);
}
void update(int a,int b,long long c,int l,int r,int rt)
{
    if(a<=l&&b>=r)
    {
        sum[rt]+=(r-l+1)*c;
        add[rt]+=c;
        return;
    }
    push_down(rt,r-l+1);
    int mid=(l+r)>>1;
    if(a<=mid)
        update(a,b,c,lson);
    if(b>mid)
        update(a,b,c,rson);
    push_up(rt);//更新區間和
}
long long query(int a,int b,int l,int r,int rt)//區間求和
{
    if(a<=l&&b>=r)
        return sum[rt];
    push_down(rt,r-l+1);
    int mid=(l+r)>>1;
    long long ans=0;
    if(a<=mid)
        ans+=query(a,b,lson);
    if(b>mid)
        ans+=query(a,b,rson);
    return ans;
}
int main()
{
  int n,m;
  scanf("%d %d",&n,&m);
  build(1,n,1);
  while(m--)
  {
      char str[2];
      int a,b;
      long long c;
      scanf("%s",str);
      if(str[0]=='C')
      {
          scanf("%d%d%lld",&a,&b,&c);
          update(a,b,c,1,n,1);
      }
      else
      {
          scanf("%d%d",&a,&b);
          printf("%lld\n",query(a,b,1,n,1));
      }
  }
  return 0;
}

3.H - 敵兵佈陣


C國的死對頭A國這段時間正在進行軍事演習,所以C國間諜頭子Derek和他手下Tidy又開始忙乎了。A國在海岸線沿直線佈置了N個工兵營地,Derek和Tidy的任務就是要監視這些工兵營地的活動情況。由於採取了某種先進的監測手段,所以每個工兵營地的人數C國都掌握的一清二楚,每個工兵營地的人數都有可能發生變動,可能增加或減少若干人手,但這些都逃不過C國的監視。 
中央情報局要研究敵人究竟演習什麼戰術,所以Tidy要隨時向Derek彙報某一段連續的工兵營地一共有多少人,例如Derek問:“Tidy,馬上彙報第3個營地到第10個營地共有多少人!”Tidy就要馬上開始計算這一段的總人數並彙報。但敵兵營地的人數經常變動,而Derek每次詢問的段都不一樣,所以Tidy不得不每次都一個一個營地的去數,很快就精疲力盡了,Derek對Tidy的計算速度越來越不滿:"你個死肥仔,算得這麼慢,我炒你魷魚!”Tidy想:“你自己來算算看,這可真是一項累人的工作!我恨不得你炒我魷魚呢!”無奈之下,Tidy只好打電話向計算機專家Windbreaker求救,Windbreaker說:“死肥仔,叫你平時做多點acm題和看多點演算法書,現在嚐到苦果了吧!”Tidy說:"我知錯了。。。"但Windbreaker已經掛掉電話了。Tidy很苦惱,這麼算他真的會崩潰的,聰明的讀者,你能寫個程式幫他完成這項工作嗎?不過如果你的程式效率不夠高的話,Tidy還是會受到Derek的責罵的.  Input 第一行一個整數T,表示有T組資料。 
每組資料第一行一個正整數N(N<=50000),表示敵人有N個工兵營地,接下來有N個正整數,第i個正整數ai代表第i個工兵營地裡開始時有ai個人(1<=ai<=50)。 
接下來每行有一條命令,命令有4種形式: 
(1) Add i j,i和j為正整數,表示第i個營地增加j個人(j不超過30) 
(2)Sub i j ,i和j為正整數,表示第i個營地減少j個人(j不超過30); 
(3)Query i j ,i和j為正整數,i<=j,表示詢問第i到第j個營地的總人數; 
(4)End 表示結束,這條命令在每組資料最後出現; 
每組資料最多有40000條命令 
Output
對第i組資料,首先輸出“Case i:”和回車, 
對於每個Query詢問,輸出一個整數並回車,表示詢問的段中的總人數,這個數保持在int以內。 
Sample Input
1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End 
Sample Output
Case 1:
6
33
59
解題思路:單點修改與區間求和,對於單點修改只要不斷的遞迴左右子樹找到他並改變他的值,然後向上傳遞修改區間的總和
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100010;
int t,n,w[N];

struct node
{
    int l,r;
    int sum;
}tr[N*4];

void pushup(int u)
{
    tr[u].sum=tr[u*2].sum+tr[u*2+1].sum;
}

void build(int u,int l,int r)
{
    if(l==r)
        tr[u]={l,r,w[r]};
    else
    {
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u*2,l,mid),build(u*2+1,mid+1,r);
        pushup(u);
    }
}

int query(int u,int l,int r)
{
    if(tr[u].l>=l&&tr[u].r<=r)
        return tr[u].sum;
    int mid=tr[u].l+tr[u].r>>1;
    int sum=0;
    if(l<=mid)
        sum=query(u*2,l,r);
    if(r>mid)
        sum+=query(u*2+1,l,r);
    return sum;
}
void modify(int u,int x,int v)
{
    if(tr[u].l==tr[u].r)
        tr[u].sum+=v;
    else
    {
        int mid=(tr[u].l+tr[u].r)>>1;
        if(x<=mid)
            modify(u<<1,x,v);
        else
            modify(u*2+1,x,v);
        pushup(u);
    }
}
int main()
{
    int i,j;
    scanf("%d",&t);
    for(j=1;j<=t;j++)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
        }
        char str[10];
        int a,b;
        build(1,1,n);
        printf("Case %d:\n",j);
        while(scanf("%s",str)&&str[0]!='E')
        {
            scanf("%d%d",&a,&b);
            if(str[0]=='Q')
                printf("%d\n",query(1,a,b));
            else if(str[0]=='A')
                modify(1,a,b);
            else if(str[0]=='S')
                modify(1,a,-b);
        }
    }
    return 0;
}

 

4.I - Minimum Inversion Number

 

The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. 

For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following: 

a1, a2, ..., an-1, an (where m = 0 - the initial seqence) 
a2, a3, ..., an, a1 (where m = 1) 
a3, a4, ..., an, a1, a2 (where m = 2) 
... 
an, a1, a2, ..., an-1 (where m = n-1) 

You are asked to write a program to find the minimum inversion number out of the above sequences. 

InputThe input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1. 
OutputFor each case, output the minimum inversion number on a single line. 
Sample Input

10
1 3 6 9 0 8 5 7 4 2

Sample Output

16
解題思路:題意大概是求逆序數的最小值,有n個序列求他們的逆序數的最小值,這n個序列是通過不斷使第一個數放到末尾構成的
我們可以從中發現規律:每次將第一個數放到最後,其該序列的逆序數=上一個序列的逆序數+n-2*a[i]-1;因為之前有a[i]個逆序數,當放到後面後原來的逆序數變成了順序
而原來順序的變成了逆序,因此需要在原來序列逆序數的基礎上+(n-a[i]-1)-a[i],因此就是上面的式子。
之後我們只需求出第一個序列的逆序數即可,然後通過一個迴圈就可以找出最小的逆序數
程式碼:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=5010;
struct node
{
    int l,r,sum;
}tr[4*N];

void push_up(int m)
{
    tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
}

void buildtree(int t,int l,int r)
{
    tr[t].l=l;
    tr[t].r=r;
    tr[t].sum=0;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    buildtree(t<<1,l,mid);
    buildtree(t<<1|1,mid+1,r);
}

int query(int t,int l,int r)
{
    if(tr[t].l>=l&&tr[t].r<=r)
        return tr[t].sum;
    int mid=(tr[t].l+tr[t].r)>>1;
    int ans=0;
    if(l<=mid)
        ans+=query(t<<1,l,r);
    if(r>mid)
        ans+=query(t<<1|1,l,r);
    return ans;
}

void update(int t,int id)
{
    if(tr[t].l==id&&tr[t].r==id)
    {
        tr[t].sum=1;
        return;
    }
    int mid=(tr[t].l+tr[t].r)>>1;
    if(id<=mid)
        update(t<<1,id);
    else
        update(t<<1|1,id);
    push_up(t);
}

int main()
{
    int n,i,j;
    int a[N];
    while(~scanf("%d",&n)&&n!=0)
    {
        buildtree(1,0,n-1);
        int sum=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=query(1,a[i]+1,n-1);//找比a[i]大的數的個數的和
            update(1,a[i]);//將該值加入到線段樹中
        }
        int ans=sum;
        for(i=0;i<n;i++)
        {
            sum+=n-2*a[i]-1;
            ans=min(ans,sum);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

5.J - Just a Hook


In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length. 



Now Pudge wants to do some operations on the hook. 

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks. 
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows: 

For each cupreous stick, the value is 1. 
For each silver stick, the value is 2. 
For each golden stick, the value is 3. 

Pudge wants to know the total value of the hook after performing the operations. 
You may consider the original hook is made up of cupreous sticks. 
InputThe input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases. 
For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations. 
Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind. 
OutputFor each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example. 
Sample Input
1
10
2
1 5 2
5 9 3
Sample Output
Case 1: The total value of the hook is 24.
解題思路:區間修改與區間求和(lazy標記)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=100010;
struct node
{
    int sum,lazy;
}tr[4*N];

void push_up(int m)
{
    tr[m].sum=tr[m<<1].sum+tr[m<<1|1].sum;
}

void push_down(int t,int l,int r)
{
    int mid=(l+r)>>1;
    tr[t<<1].lazy=tr[t<<1|1].lazy=tr[t].lazy;
    tr[t<<1].sum=tr[t].lazy*(mid-l+1);
    tr[t<<1|1].sum=tr[t].lazy*(r-mid);
    tr[t].lazy=0;
}

void buildtree(int t,int l,int r)
{
    tr[t].lazy=0;
    if(l==r)
    {
        tr[t].sum=1;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(t<<1,l,mid);
    buildtree(t<<1|1,mid+1,r);
    push_up(t);
}

void update(int t,int L,int R,int l,int r,int flag)
{
    if(L<=l&&r<=R)
    {
        tr[t].lazy=flag;
        tr[t].sum=(r-l+1)*flag;
        return;
    }
    if(tr[t].lazy)
        push_down(t,l,r);
    int mid=(l+r)>>1;
    if(L<=mid)
        update(t<<1,L,R,l,mid,flag);
    if(mid<R)
        update(t<<1|1,L,R,mid+1,r,flag);
    push_up(t);
}

long long query(int a,int b,int l,int r,int t)
{
    if(a<=l&&r<=b)
        return tr[t].sum;
    if(tr[t].lazy)
        push_down(t,l,r);
    int mid=(l+r)>>1;
    long long sum=0;
    if(a<=mid)
        sum+=query(a,b,l,mid,t<<1);
    if(b>mid)
        sum+=query(a,b,mid+1,r,t<<1|1);
    return sum;
}

int main()
{
    int t,i,j;
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    {
        int n,q;
        scanf("%d%d",&n,&q);
        buildtree(1,1,n);
        while(q--)
        {
            int a,b,flag;
            scanf("%d%d%d",&a,&b,&flag);
            update(1,a,b,1,n,flag);
        }
        printf("Case %d: The total value of the hook is %d.\n",i,tr[1].sum);
    }
    return 0;
}

6.A - I Hate It

很多學校流行一種比較的習慣。老師們很喜歡詢問,從某某到某某當中,分數最高的是多少。 
這讓很多學生很反感。 

不管你喜不喜歡,現在需要你做的是,就是按照老師的要求,寫一個程式,模擬老師的詢問。當然,老師有時候需要更新某位同學的成績。

Input本題目包含多組測試,請處理到檔案結束。 
在每個測試的第一行,有兩個正整數 N 和 M ( 0<N<=200000,0<M<5000 ),分別代表學生的數目和操作的數目。 
學生ID編號分別從1編到N。 
第二行包含N個整數,代表這N個學生的初始成績,其中第i個數代表ID為i的學生的成績。 
接下來有M行。每一行有一個字元 C (只取'Q'或'U') ,和兩個正整數A,B。 
當C為'Q'的時候,表示這是一條詢問操作,它詢問ID從A到B(包括A,B)的學生當中,成績最高的是多少。 
當C為'U'的時候,表示這是一條更新操作,要求把ID為A的學生的成績更改為B。 
Output對於每一次詢問操作,在一行裡面輸出最高成績。Sample Input

5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5

Sample Output

5
6
5
9

 解題思路:線段樹求區間最大與單點修改



#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=200010;
struct node
{
    int l,r,ans;
}tr[N*4];
int pre[N];
void buildtree(int t,int l,int r)
{
    tr[t].l=l;
    tr[t].r=r;
    if(l==r)
    {
        tr[t].ans=pre[l];//葉子節點賦值
        return;
    }
    int mid=(l+r)>>1;
    buildtree(t<<1,l,mid);
    buildtree(t<<1|1,mid+1,r);
    tr[t].ans=max(tr[t<<1].ans,tr[t<<1|1].ans);//求出區間最大
}
void update(int t,int id,int key)//單點修改
{
    if(tr[t].l==id&&tr[t].r==id)
    {
        tr[t].ans=key;
        return;
    }
    int mid=(tr[t].l+tr[t].r)>>1;
    if(id<=mid)
        update(t<<1,id,key);
    else
        update(t<<1|1,id,key);
   tr[t].ans=max(tr[t<<1].ans,tr[t<<1|1].ans);//更新區間的最大值
}
int query(int t,int l,int r)//區間求最大值
{
    if(tr[t].l>=l&&tr[t].r<=r)
        return tr[t].ans;
    int mid=(tr[t].l+tr[t].r)>>1;
    int ans=-100;
    if(l<=mid)//找出左半邊的最大值
        ans=query(t<<1,l,r);
    if(r>mid)//將作半邊的最大值與右半邊的最大值比較
        ans=max(ans,query(t<<1|1,l,r));
    return ans;
}
int main()
{
    int n,m,i;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%d",&pre[i]);
        }
        buildtree(1,1,n);
        while(m--)
        {
            char s[2];
            int a,b;
            scanf("%s",&s);
            scanf("%d%d",&a,&b);
            if(s[0]=='Q')
            {
                printf("%d\n",query(1,a,b));
            }
            if(s[0]=='U')
            {
                update(1,a,b);
            }
        }
    }
    return 0;
}

7.B - Billboard


At the entrance to the university, there is a huge rectangular billboard of size h*w (h is its height and w is its width). The board is the place where all possible announcements are posted: nearest programming competitions, changes in the dining room menu, and other important information. 

On September 1, the billboard was empty. One by one, the announcements started being put on the billboard. 

Each announcement is a stripe of paper of unit height. More specifically, the i-th announcement is a rectangle of size 1 * wi. 

When someone puts a new announcement on the billboard, she would always choose the topmost possible position for the announcement. Among all possible topmost positions she would always choose the leftmost one. 

If there is no valid location for a new announcement, it is not put on the billboard (that's why some programming contests have no participants from this university). 

Given the sizes of the billboard and the announcements, your task is to find the numbers of rows in which the announcements are placed.
InputThere are multiple cases (no more than 40 cases). 

The first line of the input file contains three integer numbers, h, w, and n (1 <= h,w <= 10^9; 1 <= n <= 200,000) - the dimensions of the billboard and the number of announcements. 

Each of the next n lines contains an integer number wi (1 <= wi <= 10^9) - the width of i-th announcement.OutputFor each announcement (in the order they are given in the input file) output one number - the number of the row in which this announcement is placed. Rows are numbered from 1 to h, starting with the top row. If an announcement can't be put on the billboard, output "-1" for this announcement.Sample Input
3 5 5
2
4
3
3
3
Sample Output
1
2
1
3
-1
程式碼:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=200010;
int maxx[N*4];
int h,w,n;

void push_up(int m)
{
    maxx[m]=max(maxx[m<<1],maxx[m<<1|1]);
}

void build(int t,int l,int r)
{
    if(l==r)
    {
        maxx[t]=w;
        return;
    }
    int mid=(l+r)>>1;
    build(t<<1,l,mid);
    build(t<<1|1,mid+1,r);
    push_up(t);
}
int update(int t,int val,int l,int r)
{
    if(val>maxx[t])
        return -1;
    if(l==r)
    {
        if(val<=maxx[t])
        {
            maxx[t]-=val;
            return l;
        }
        else
            return -1;
    }
    int mid=(l+r)>>1;
    int flag=-1;
    flag=update(t<<1,val,l,mid);
    if(flag<0)
        flag=update(t<<1|1,val,mid+1,r);
    push_up(t);
    return flag;
}
int main()
{
    int i,j;
    while(scanf("%d%d%d",&h,&w,&n)!=EOF)
    {
        if(h>n)
            h=n;
        build(1,1,h);
        int val;
        int k;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&val);
            printf("%d\n",update(1,val,1,h));
        }
    }
    return 0;
}

 

總結:lazy標記線段樹需要加強,區間修改操作的不熟練需要加強。



 

&n