20200713 T1序列问题

题目描述

\(\text{Tom}\) 的梦想是成为世界冠军。因此他每天都在认真做题。

某天他在学数学的时候又造出了一个新题,并准备以此为难你一下。

给定长度为 \(n\) 的序列,正整数 \(k\),可以定义两个函数:

\(F(i)=i^{a[i]} \ mod \ k\)

\(G(i)=a[i]^{i} \ mod \ k\)

现在要求计算\((l,r)\)数对的数量,其中 \(1 \leq l < r \leq n\),且 \(f(l)>g(r)\)

输入格式

\(1\)\(2\) 个整数 \(n\)\(k\)

\(2\) 行读入 \(n\) 个整数 \(a_1,a_2 \dots a_n\)

输出格式

一行输出答案。

样例

input1

5 10000
3 1 5 4 2

output1

2

数据规模和限制

对于全部测试数据,满足 \(N \leq 10^5\)\(k \leq 10^9\)\(a_i \leq 10^9\)

各个测试点的数据规模及特殊性质如下表。

测试点 N \(a_i\)
\(1 \sim 3\) \(\leq 100\) \(\leq 100\)
\(4 \sim 6\) \(\leq 3000\) \(\leq 10 ^ 5\)
\(7 \sim 10\) \(\leq 10 ^ 5\) \(\leq 10 ^ 9\)

题解

先计算出 \(f[i]\)\(g[i]\),然后离散化一下,放到权值线段树上找逆序对个数。
别的找逆序对的方法也行

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 300001
#define lson now << 1
#define rson now << 1 | 1

long long ans;
int n, mod, a[MAXN], f[MAXN], g[MAXN];
struct lsh {
	int w, num;
	friend bool operator < (lsh x, lsh y) {
		return x.w < y.w;
	}
}b[MAXN];
struct Node {
	int l, r, w;
}tree[MAXN << 1];

void build(int l, int r, int now) {
	tree[now].l = l, tree[now].r = r;
	if (tree[now].l == tree[now].r) {
		tree[now].w = 0;
		return;
	}
	int mid = (tree[now].l + tree[now].r) >> 1;
	build(l, mid, lson), build(mid + 1, r, rson);
	tree[now].w = tree[lson].w + tree[rson].w;
}

void add(int x, int now) {
	if (tree[now].l == tree[now].r) {
		++tree[now].w;
		return;
	}
	int mid = (tree[now].l + tree[now].r) >> 1;
	if (x <= mid) add(x, lson);
	else add(x, rson);
	tree[now].w = tree[lson].w + tree[rson].w;
}

int query(int x, int y, int now) {
	if (tree[now].l >= x && tree[now].r <= y) return tree[now].w;
	int mid = (tree[now].l + tree[now].r) >> 1, ans = 0;
	if (x <= mid) ans += query(x, y, lson);
	if (y > mid) ans += query(x, y, rson);
	return ans;
}

int qpow(int a, int b) {
	int ans = 1, base = a;
	while(b) {
		if (b & 1) ans = 1ll * ans * base % mod;
		base = 1ll * base * base % mod;
		b >>= 1;
	}
	return ans % mod;
}

int main() {
	scanf("%d %d", &n, &mod);
	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
	for (int i = 1; i <= n; ++i) {
		f[i] = qpow(i, a[i]);
		b[i].w = f[i];
		b[i].num = i; 
		g[i] = qpow(a[i], i);
		b[i + n].w = g[i];
		b[i + n].num = -i;
	}
	std::sort(b + 1, b + 2 * n + 1);
	int now = 0;
	for (int i = 1; i <= 2 * n; ++i) {
		if (b[i].w != b[i - 1].w) ++now;
		if (b[i].num > 0) f[b[i].num] = now;
		else g[-b[i].num] = now;
	}
	build(1, 2 * n, 1);
	for (int i = n; i > 1; --i) {
		add(g[i], 1);
		if (f[i - 1] == 1) continue;
		ans += query(1, f[i - 1] - 1, 1);
	}
	printf("%lld\n", ans);
	return 0;
}

反思

考场上以为自己写了个 60pts 的算法实际上是 30pts 的算法。写完之后一定要检查啊qwq。

posted @ 2020-07-13 20:28  yu__xuan  阅读(198)  评论(0编辑  收藏  举报