【NOIP2017模拟8.8A组】Trip

本文介绍了一种利用笛卡儿树和LCA算法解决特定区间查询问题的方法。通过对笛卡儿树的构建及性质分析,结合LCA算法进行区间最大值查询优化,实现高效解答。该算法适用于大规模数据集,能够在线性时间内完成处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

给出一个有n个互不相同元素,有m个询问,每次给出l,r,
询问ri=l[a[i]>max(al..i1)]or[a[i]>max(ai+1..r)]
1<=n<=10^6

题解:

建一棵笛卡儿树,根的a是最大的。
根据笛卡儿树的性质可得:
1.区间[l..r]里的点一定在l到r的最短路上。
2.对于l到lca的路径上的点,如果是它父亲节点的左节点,对答案贡献1。r的刚好相反。
于是只需要打个能维护lca的算法即可。
tajan可以做到:O(n α(n))
log算法也可以过。

比赛时yy了一个和笛卡儿树很像的搜索森林,加上二分是log的,算法需要离线,在这里放出来标:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

void read(int &x)
{
       int sig=1;
       char c;
       for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
       x = 0;
       for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
       x *= sig;
}

void write(int x)
{
       if (!x) putchar('0');else
       {
              char s[10];
              int i,j=0;
              for (;x>0;x/=10) s[j++]=x%10;
              for (i=j-1;i>=0;i--) putchar(s[i]+48);
       }
       putchar('\n');
}

const int N = 1000005;

int n, m, a[N], d[N * 2], ans[N];
struct node {
    int x, y;
}b[N * 2];
int tot, final[N], to[N], next[N], num[N];
struct Ans {
    int en, g;
}c[N];

void link(int x, int y, int z) {
    next[++ tot] = final[x]; to[tot] = y; num[tot] = z; final[x] = tot;
}

int final2[N], to2[N], next2[N], tot2;

void link2(int x, int y) {
    next2[++ tot2] = final2[x]; to2[tot2] = y;  final2[x] = tot2;
}

int z2[N], z0;

void dg(int x, int en) {
    z0 = 1; d[1] = x; z2[1] = 0;
    while(z0 > 0) {
        int bx = 0;
        if(z2[z0] == 0) {
            x = d[z0];
            for(int i = final[x]; i; i = next[i]) {
                int y = to[i], z = num[i];
                for(int l = 1, r = z0; l <= r; ) {
                    int m = (l + r) / 2;
                    if(d[m] <= y) c[z].g = z0 - m + 1, c[z].en = d[m], r = m - 1; else l = m + 1; 
                }
            }
            for(int i = final2[x]; i; i = next2[i]) {
                z2[z0] = i, d[++ z0] = to2[i], z2[z0] = 0;
                bx = 1; break;
            }
        } else {
            for(int i = next2[z2[z0]]; i; i = next2[i]) {
                z2[z0] = i, d[++ z0] = to2[i], z2[z0] = 0;
                bx = 1; break;
            }
        }
        if(!bx) z0 --;
    }
}

int bz[N];

void Solve() {
    int st = 1, en = 0;
    memset(bz, 0, sizeof(bz));
    memset(final2, 0, sizeof(final2));
    memset(next2, 0, sizeof(next2));
    tot2 = 0;
    fd(i, n, 1) {
        while(st <= en && a[d[en]] < a[i]) en --;
        if(st > en) bz[i] = 1; else link2(d[en], i);
        d[++ en] = i;
    }
    fo(i, 1, n) if(bz[i])
        d[1] = i, dg(i, 1);
}

int main() {
    freopen("trip.in", "r", stdin);
    freopen("trip.out", "w", stdout);
    scanf("%d %d", &n, &m);
    fo(i, 1, n) read(a[i]);
    fo(i, 1, m) read(b[i].x), read(b[i].y);
    fo(i, 1, m) link(b[i].x, b[i].y, i);
    Solve();
    memset(final, 0, sizeof(final));
    memset(next, 0, sizeof(next));
    tot = 0;
    fo(i, 1, m) {
        ans[i] = c[i].g;
        c[i].g = 0;
        if(c[i].en < b[i].y) link(n - b[i].y + 1, n - c[i].en, i);
    }
    fo(i, 1, n / 2) swap(a[i], a[n - i + 1]);
    Solve();
    fo(i, 1, m) ans[i] += c[i].g, write(ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值