[2018 Multi-University Training Contest 5] HDU 6356 Glad You Came(倍增思想逆運用?)

題目鏈接

Glad You Came

題目大意

T組數據。
每組給一個長度爲n 的數組, 初始全爲0, 有m 次操作, 每次操作將區間[l,r]a<v 的更新成v。
最後詢問i=1n(i×ai)
輸入的數據由題目給的方式生成。

數據範圍

1T100
1n105
1m5×106

解題思路

比賽的時候這道題是沒寫出來的, 看到了數據範圍肯定是卡線段樹, 線段樹需要的複雜度是mlog(n) 再加上T組。 顯然TLE, 最後也沒有想到正解。
賽後看了一下題解, 豁然開朗, 居然可以這麼做, 複雜度一下就降到了nlog(n)

對於每一段區間[l,r] , 可以將其轉換爲兩段二的冪次的長度。
d=[log2(r1+1)] , 則[l,r] 可拆成[l,l+2d1][r2d+1,r] , 兩個區間, 保證了完全覆蓋。 (就是RMQ的思想)。

這樣一共最多有log(n) 種線段,從長度從大到小枚舉, 每一個線段可以轉換爲等分的兩部分的線段, 對於完全相同的線段, 維護最大值即可。 這樣一直做到長度爲1的線段就結束了。
用數組存一下, 那麼Line(i,d) 表示從i 開始2d 這麼長的線段的攜帶的最大值。
每次則可以轉移爲

Line(i,d1)=max(Line(i,d1),Line(i,d))Line(i+2d1,d1)=max(Line(i+2d1,d1),Line(i,d))

最後按照題目要求計算答案即可。

還有就是題目給的生成數據的方式有點坑, 非常慢.
其實可以不用求出f 數組, 每次計算三個值就可以了。 不需要存起來。 同時也可以減少模n 的次數。

AC代碼

/********************************************
 *Author*        :ZZZZone
 *Created Time*  : 一  8/ 6 20:26:08 2018
 * Ended  Time*  : 一  8/ 6 20:52:16 2018
*********************************************/

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define debug(x) std::cerr << #x << " = " << (x) << std::endl
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;

inline void OPEN(string s){
    freopen((s + ".in").c_str(), "r", stdin);
    freopen((s + ".out").c_str(), "w", stdout);
}

const int MAXN = 1e5;
const int MAXV = 5e6;
const int INF = 1 << 30;

unsigned X, Y, Z;
int n,m;

int Log2[MAXN+5];
int pow2[30];
LL Line[MAXN+5][35];

unsigned getnum(){
    X = X ^ (X << 11);
    X = X ^ (X >> 4);
    X = X ^ (X << 5);
    X = X ^ (X >> 14);
    unsigned W = X ^ (Y ^ Z);
    X = Y;
    Y = Z;
    Z = W;
    return Z;
}

void Init(){
    scanf("%d %d", &n, &m);
    scanf("%u %u %u", &X, &Y, &Z);
    for(int i = 1; i <= m; i++){
        int p = getnum() % n + 1, q = getnum() % n + 1;
        int l = min(p, q);
        int r = max(p, q);
        LL v = getnum() % INF;
        int d = Log2[r-l+1];
        Line[l][d] = max(Line[l][d], (LL)v);
        Line[r-pow2[d] + 1][d] = max(Line[r-pow2[d] + 1][d], (LL)v);
    }
}

void Solve(){
    for(int d = 30; d >= 1; d--){
        for(int i = 1; i +pow2[d] <= n+1; i++){
            Line[i][d-1] = max(Line[i][d-1], Line[i][d]);
            Line[i+pow2[d-1]][d-1] = max(Line[i+pow2[d-1]][d-1], Line[i][d]);
            Line[i][d] = 0LL;
        }
    }
    LL ans = 0LL;
    for(int i = 1; i <= n; i++){
        ans = ans ^ ((LL)i * Line[i][0]);
        Line[i][0] = 0LL;
    }
    printf("%lld\n", ans);
}

int main()
{
    Log2[0] = -1;
    pow2[0] = 1;
    for(int i = 1; i <= 30; i++) pow2[i] = pow2[i-1] * 2;
    for(int i = 1; i <= MAXN; i++) Log2[i] = Log2[i >> 1] + 1;
    int T; scanf("%d", &T);
    while(T--){
        Init();
        Solve();
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章