#27474: Java TLE問題???


r1cky (hehe)

學校 : 國立臺灣師範大學
編號 : 158637
來源 : [140.122.136.123]
最後登入時間 :
2024-04-25 13:42:09
f986. Polynomial Queries -- CSES1736 | From: [114.32.128.128] | 發表日期 : 2021-10-08 22:13

我用線段樹加懶標,結果沒過(複雜度的問題??),而且今天zj好奇怪,前一個小時之前丟可以拿20%(通過兩個剩下TLE),再丟一次相同的程式變8%(只過第一個),還是其實這題不能用線段樹?

 
#27478: Re:Java TLE問題???


jam930725@gmail.com (浮沉沉沉沉沉沉沉沉)

學校 : 國立臺中科技大學
編號 : 124762
來源 : [123.240.115.224]
最後登入時間 :
2022-08-27 13:56:53
f986. Polynomial Queries -- CSES1736 | From: [101.127.206.20] | 發表日期 : 2021-10-09 09:29

我用線段樹加懶標,結果沒過(複雜度的問題??),而且今天zj好奇怪,前一個小時之前丟可以拿20%(通過兩個剩下TLE),再丟一次相同的程式變8%(只過第一個),還是其實這題不能用線段樹?

我用線段樹+懶人標記過的(剛剛丟了一次 AC 1.4s)

 
#27489: Re:Java TLE問題???


r1cky (hehe)

學校 : 國立臺灣師範大學
編號 : 158637
來源 : [140.122.136.123]
最後登入時間 :
2024-04-25 13:42:09
f986. Polynomial Queries -- CSES1736 | From: [114.32.128.128] | 發表日期 : 2021-10-09 22:04

我用線段樹加懶標,結果沒過(複雜度的問題??),而且今天zj好奇怪,前一個小時之前丟可以拿20%(通過兩個剩下TLE),再丟一次相同的程式變8%(只過第一個),還是其實這題不能用線段樹?

我用線段樹+懶人標記過的(剛剛丟了一次 AC 1.4s)


那想請教你大概是怎麼做到的(像是你懶人標記是記什麼東西),因為其實我是第一次實作懶人標記,所以其實還不太懂到底怎麼寫比較好

 
#27500: Re:Java TLE問題???


jam930725@gmail.com (浮沉沉沉沉沉沉沉沉)

學校 : 國立臺中科技大學
編號 : 124762
來源 : [123.240.115.224]
最後登入時間 :
2022-08-27 13:56:53
f986. Polynomial Queries -- CSES1736 | From: [123.110.34.107] | 發表日期 : 2021-10-10 12:11

那想請教你大概是怎麼做到的(像是你懶人標記是記什麼東西),因為其實我是第一次實作懶人標記,所以其實還不太懂到底怎麼寫比較好

我們可以先思考一個問題 如果今天題目的區間修改變成:"針對該區間[a,b]的每個元素 數值增加k" lazyTage需要怎麼紀錄數值

很簡單 區間[a,b]的元素數量 * k

現在回來看題目 我們的目標 就是想辦法去記錄 剛才的"區間[a,b]的元素數量 * k" (又或者說怎麼去獲得這個值)

假設現在有一個長度為n 編號為(1~n)的區間 現在從中挑出一個區間[a,b] 來進行觀察

我們按照題目的修改方式 對於區間[a,b]進行一次修改 這時[a,b] 所增加的數值為 1+2+3...+b

也就是 (b+1)*b/2,公差d = 1

接著我們對 [a-1, b] 進行一次修改 我們可以觀察到 每個元素各自增加的值如下

a:(1+2)=3  a+1:(2+3)=5   a+2:(3+4)=7 ... b:(b+b+1)

總和變成 (2b+1 + 3)*b / 2,公差d = 2

2b+1 也可以寫成 3+(b-a)*d

也就是說 求出區間總和 我們需要: 1.公差 2.首相的值(a所需要增加的值)  3.區間元素的數量

而公差就是修改的次數 

我們需要去紀錄的值有兩樣:公差 首相,所以我們需要2個lazyTage (我把這兩個用class綁在一起)

public static class LazyTage{

    int count = 0;//修改次數

    long num = 0;//增加的值

    public Lazy(){

    }

    public void add(long n, int c){

        count += c;

        num += n;

    }

    public void add(long n){

        count++;

        num += n;

    }

    public void clear(){

        num = count = 0;

    }

}

把lazyTage下推的過程如下:

public static void pushDown(int ind, int leftNodeNum, int rightNodeNum){ //index, 左節點數量, 右節點數量

    if(lazy[ind].num > 0){ //如果有需要修改

        tree[ind << 1] += diff(lazy[ind].num, leftNodeNum, lazy[ind].count); //解等差級數和

        tree[(ind << 1) + 1] += diff(lastNum(lazy[ind].num, leftNodeNum+1, lazy[ind].count), rightNodeNum, lazy[ind].count); 

        //lastNum 是拿來計算末項的 左區間的末項+d = 右區間的首相

        

        lazy[ind << 1].add(lazy[ind].num, lazy[ind].count);

        lazy[(ind << 1) + 1].add(lastNum(lazy[ind].num, leftNodeNum+1, lazy[ind].count), lazy[ind].count);

        

        lazy[ind].clear();

    }

}

public static long diff(long a, long b, long diff){ //首相的值, 節點數量, 公差

    long last = lastNum(a, b, diff);

    return ((a+last)*b) >> 1;

}

public static long lastNum(long s, long n, long diff){ 

    return s + (n-1)*dif;

} 

在update時 [a,b]被包含在需要被修改之區間中的時候:

if(l >= changeLeft && r <= changeRight){

    int a = l-changeLeft+1;

    tree[ind] += diff(a, r-l+1, 1);

    lazy[ind].add(a);

    return;

}

剩下的 基本上就和普通的區間修改差不多

 

以上 希望有幫到你

 
#27565: Re:Java TLE問題???


r1cky (hehe)

學校 : 國立臺灣師範大學
編號 : 158637
來源 : [140.122.136.123]
最後登入時間 :
2024-04-25 13:42:09
f986. Polynomial Queries -- CSES1736 | From: [114.32.128.128] | 發表日期 : 2021-10-14 21:59

 

我們可以先思考一個問題 如果今天題目的區間修改變成:"針對該區間[a,b]的每個元素 數值增加k" lazyTage需要怎麼紀錄數值

很簡單 區間[a,b]的元素數量 * k

現在回來看題目 我們的目標 就是想辦法去記錄 剛才的"區間[a,b]的元素數量 * k" (又或者說怎麼去獲得這個值)

假設現在有一個長度為n 編號為(1~n)的區間 現在從中挑出一個區間[a,b] 來進行觀察

我們按照題目的修改方式 對於區間[a,b]進行一次修改 這時[a,b] 所增加的數值為 1+2+3...+b

也就是 (b+1)*b/2,公差d = 1

接著我們對 [a-1, b] 進行一次修改 我們可以觀察到 每個元素各自增加的值如下

a:(1+2)=3  a+1:(2+3)=5   a+2:(3+4)=7 ... b:(b+b+1)

總和變成 (2b+1 + 3)*b / 2,公差d = 2

2b+1 也可以寫成 3+(b-a)*d

也就是說 求出區間總和 我們需要: 1.公差 2.首相的值(a所需要增加的值)  3.區間元素的數量

而公差就是修改的次數 

我們需要去紀錄的值有兩樣:公差 首相,所以我們需要2個lazyTage (我把這兩個用class綁在一起)

public static class LazyTage{

    int count = 0;//修改次數

    long num = 0;//增加的值

    public Lazy(){

    }

    public void add(long n, int c){

        count += c;

        num += n;

    }

    public void add(long n){

        count++;

        num += n;

    }

    public void clear(){

        num = count = 0;

    }

}

把lazyTage下推的過程如下:

public static void pushDown(int ind, int leftNodeNum, int rightNodeNum){ //index, 左節點數量, 右節點數量

    if(lazy[ind].num > 0){ //如果有需要修改

        tree[ind << 1] += diff(lazy[ind].num, leftNodeNum, lazy[ind].count); //解等差級數和

        tree[(ind << 1) + 1] += diff(lastNum(lazy[ind].num, leftNodeNum+1, lazy[ind].count), rightNodeNum, lazy[ind].count); 

        //lastNum 是拿來計算末項的 左區間的末項+d = 右區間的首相

        

        lazy[ind << 1].add(lazy[ind].num, lazy[ind].count);

        lazy[(ind << 1) + 1].add(lastNum(lazy[ind].num, leftNodeNum+1, lazy[ind].count), lazy[ind].count);

        

        lazy[ind].clear();

    }

}

public static long diff(long a, long b, long diff){ //首相的值, 節點數量, 公差

    long last = lastNum(a, b, diff);

    return ((a+last)*b) >> 1;

}

public static long lastNum(long s, long n, long diff){ 

    return s + (n-1)*dif;

} 

在update時 [a,b]被包含在需要被修改之區間中的時候:

if(l >= changeLeft && r <= changeRight){

    int a = l-changeLeft+1;

    tree[ind] += diff(a, r-l+1, 1);

    lazy[ind].add(a);

    return;

}

剩下的 基本上就和普通的區間修改差不多

 

以上 希望有幫到你

太感謝了!!感謝你花那麼多時間和心思回應問題,寫的很精采,現在終於過了!

對於怎麼求等差級數和,我當時還煩惱了很久到底怎麼懶標記加起來,原來是利用d(公差)跟a(首項)紀錄,之後走訪的時候再利用等差級數和的公式計算!!

 
ZeroJudge Forum