BZOJ3956: Count

orz 出题人Gromah

这道题我的算法和他的标解完全不一样,目前在bzoj上是第一名,空间开销也是当前最小的(截个图纪念一下)

 

首先用单调栈$O(n)$地预处理出以每个点为左端点、右端点的可行点对个数。

然后对于每个询问$[l, r]$中,我们找到其中权值最大的点,假设它是$p$

然后可以发现一个性质,在$[l, r]$中的所有可行点对都不会穿过$p$,如果穿过的话因为$p$是权值最大的点所以不合法

于是我们只要统计$[l, p - 1]$的所有点作为左端点时的合法点对和$[p + 1, r]$的所有点作为右端点时的合法点对数量即可

用ST表维护区间最大值

时间复杂度$O(nlogn + m)$,空间复杂度$O(nlogn)$

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cmath>
 4 #include <cstring>
 5 #include <algorithm>
 6 #include <map>
 7 using namespace std;
 8 #define rep(i, l, r) for (int i = l; i <= r; i++)
 9 #define drep(i, r, l) for (int i = r; i >= l; i--)
10 typedef long long ll;
11 const int N = 3e5 + 8;
12 int n, m, ty, logn, st[22][N], L[N], R[N], son[N], a[N], top, q[N];
13 ll sumL[N], sumR[N];
14 int getint()
15 {
16     char c; int num = 0, w = 1;
17     for (c = getchar(); !isdigit(c) && c != '-'; c = getchar());
18     if (c == '-') c = getchar(), w = -1;
19     for (;isdigit(c); c = getchar()) num = num * 10 + c - '0';
20     return num * w;
21 }
22 void init()
23 {
24     logn = (int)(log(n) / log(2.0));
25     rep(i, 1, n)
26     {
27         son[i] = son[i - 1];
28         if ((1 << son[i] + 1) <= i) son[i]++; 
29     }
30     rep(i, 1, n) st[0][i] = i;
31     rep(i, 1, logn)
32         rep(j, 1, n)
33         {
34             int x = st[i - 1][j], y = st[i - 1][j + (1 << i - 1)];
35             if (a[x] > a[y]) st[i][j] = x;
36             else st[i][j] = y;
37         }
38     q[top = 1] = 1;
39     rep(i, 2, n)
40     {
41         while (top && a[i] > a[q[top]]) L[i]++, top--;
42         if (top) L[i]++;
43         while (top && a[i] >= a[q[top]]) top--;
44         q[++top] = i;
45     }
46     rep(i, 1, n) sumL[i] = sumL[i - 1] + L[i];
47     q[top = 1] = n;
48     drep(i, n - 1, 1)
49     {
50         while (top && a[i] > a[q[top]]) R[i]++, top--;
51         if (top) R[i]++;
52         while (top && a[i] >= a[q[top]]) top--;
53         q[++top] = i;   
54     }
55     rep(i, 1, n) sumR[i] = sumR[i - 1] + R[i];
56 }
57 int query(int l, int r)
58 {
59     int k = son[r - l + 1];
60     int x = st[k][l], y = st[k][r - (1 << son[r - l + 1]) + 1];
61     if (a[x] > a[y]) return x;
62     return y;
63 }
64 ll solve(int l, int r)
65 {
66     int p = query(l, r);
67     ll ret = sumR[p - 1] - sumR[l - 1] + sumL[r] - sumL[p];
68     return ret;
69 }
70 int main()
71 {
72 #ifndef ONLINE_JUDGE
73     freopen("input.txt", "r", stdin);
74     //freopen("output.txt", "w", stdout);
75 #endif
76     scanf("%d%d%d", &n, &m, &ty);
77     rep(i, 1, n) a[i] = getint();
78     init();
79     ll ans = 0;
80     while (m--)
81     {
82         int l = getint(), r = getint(); 
83         if (ty == 1) l = (l + ans - 1) % n + 1, r = (r + ans - 1) % n + 1;
84         if (l > r) swap(l, r);
85         ans = solve(l, r);
86         printf("%lld\n", ans);
87     }
88 #ifndef ONLINE_JUDGE
89     fclose(stdin); fclose(stdout);
90 #endif
91     return 0;
92 }
BZOJ3956

 

 

 

转载于:https://www.cnblogs.com/Dyzerjet/p/4452549.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值