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

题目链接

Glad You Came

题目大意

T组数据。
每组给一个长度为 n n 的数组, 初始全为0, 有m次操作, 每次操作将区间 [l,r] [ l , r ] a<v a < v 的更新成v。
最后询问 ni=1(i×ai) ⊕ i = 1 n ( i × a i )
输入的数据由题目给的方式生成。

数据范围

1T100 1 ≤ T ≤ 100
1n105 1 ≤ n ≤ 10 5
1m5×106 1 ≤ m ≤ 5 × 10 6

解题思路

比赛的时候这道题是没写出来的, 看到了数据范围肯定是卡线段树, 线段树需要的复杂度是 mlog(n) m ∗ l o g ( n ) 再加上T组。 显然TLE, 最后也没有想到正解。
赛后看了一下题解, 豁然开朗, 居然可以这么做, 复杂度一下就降到了 nlog(n) n ∗ l o g ( n )

对于每一段区间 [l,r] [ l , r ] , 可以将其转换为两段二的幂次的长度。
d=[log2(r1+1)] d = [ log 2 ⁡ ( r − 1 + 1 ) ] , 则 [l,r] [ l , r ] 可拆成 [l,l+2d1] [ l , l + 2 d − 1 ] [r2d+1,r] [ r − 2 d + 1 , r ] , 两个区间, 保证了完全覆盖。 (就是RMQ的思想)。

这样一共最多有 log(n) l o g ( n ) 种线段,从长度从大到小枚举, 每一个线段可以转换为等分的两部分的线段, 对于完全相同的线段, 维护最大值即可。 这样一直做到长度为1的线段就结束了。
用数组存一下, 那么 Line(i,d) L i n e ( i , d ) 表示从 i i 开始2d这么长的线段的携带的最大值。
每次则可以转移为

Line(i,d1)=max(Line(i,d1),Line(i,d))Line(i+2d1,d1)=max(Line(i+2d1,d1),Line(i,d)) L i n e ( i , d − 1 ) = m a x ( L i n e ( i , d − 1 ) , L i n e ( i , d ) ) L i n e ( i + 2 d − 1 , d − 1 ) = m a x ( L i n e ( i + 2 d − 1 , d − 1 ) , L i n e ( i , d ) )

最后按照题目要求计算答案即可。

还有就是题目给的生成数据的方式有点坑, 非常慢.
其实可以不用求出 f 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值