题意:1e5的数组 计算有多少对 ai * aj <= max(ai ai+1...aj-1 aj)
题解:在处理这种涉及到区间极值的题时 好像是个套路分治 从级值中间分成两个区间
从区间短的那边暴力枚举算贡献 然后再分治下去
可以估计复杂度 一个点最多枚举n/2次 两个点最多枚举n/4次 4个点最多枚举n/8次...
枚举加起来的复杂度是nlogn
假设枚举了ai作为一个区间端点 问题就转化为统计极值另一边的区间找 <= zd / ai的个数
我是用主席树 然后写了个类似整体二分求的 看别人题解直接用的树状数组离线做显然复杂度更优秀
总共复杂度是nlognlogn
#include#include #include #include using namespace std; typedef long long ll; const int MAXN = 1e5 + 5; ll ans; int n, cnt, len; int pre[MAXN]; int a[MAXN], b[MAXN], t[MAXN]; int zd[MAXN][25]; int ls[MAXN << 5], rs[MAXN << 5], sum[MAXN << 5]; int build(int l, int r) { int rt = ++cnt; int mid = l + r >> 1; sum[rt] = 0; if(l < r) { ls[rt] = build(l, mid); rs[rt] = build(mid + 1, r); } return rt; } int add(int o, int l, int r, int k) { int rt = ++cnt; int mid = l + r >> 1; ls[rt] = ls[o]; rs[rt] = rs[o]; sum[rt] = sum[o] + 1; if(l < r) if(k <= mid) ls[rt] = add(ls[o], l, mid, k); else rs[rt] = add(rs[o], mid + 1, r, k); return rt; } int query(int ql, int qr, int l, int r, int x) { if(l == r) return b[l] <= x ? sum[qr] - sum[ql] : 0; int mid = l + r >> 1; if(x <= b[mid]) return query(ls[ql], ls[qr], l, mid, x); else return sum[ls[qr]] - sum[ls[ql]] + query(rs[ql], rs[qr], mid + 1, r, x); } void solve(int l, int r) { if(l >= r) return; int lg = log2(r - l + 1); int index; if(a[zd[l][lg]] > a[zd[r - (1 << lg) + 1][lg]]) index = zd[l][lg]; else index = zd[r - (1 << lg) + 1][lg]; if(index - l < r - index) { for(int i = l; i < index; i++) { //if(a[i] == 1) ans++; int now = a[index] / a[i]; ans += 1LL * query(t[index], t[r], 1, len, now); } } else { for(int i = index + 1; i <= r; i++) { //if(a[i] == 1) ans++; int now = a[index] /a[i]; ans += 1LL * query(t[l - 1], t[index - 1], 1, len, now); } } if(r - l + 1 >= 2) ans += 1LL * (pre[r] - pre[index] + pre[index - 1] - pre[l - 1]); solve(l, index - 1); solve(index + 1, r); } int main() { cnt = 0; ans = 0; scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i]; for(int i = 1; i <= n; i++) zd[i][0] = i; for(int i = 1; i <= n; i++) { pre[i] = pre[i - 1]; if(a[i] == 1) pre[i]++; } for(int j = 1; j <= 20; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) if(a[zd[i][j - 1]] > a[zd[i + (1 << j - 1)][j - 1]]) zd[i][j] = zd[i][j - 1]; else zd[i][j] = zd[i + (1 << j - 1)][j - 1]; sort(b + 1, b + 1 + n); len = unique(b + 1, b + 1 + n) - b - 1; t[0] = build(1, len); for(int i = 1; i <= n; i++) { int tt = lower_bound(b + 1, b + 1 + len, a[i]) - b; t[i] = add(t[i - 1], 1, len, tt); } solve(1, n); for(int i = 1; i <= n; i++) if(a[i] == 1) ans++; printf("%lld\n", ans); return 0; }