【比赛链接】
【题解链接】
**【A】**The Rank
【思路要点】
- 按照题意模拟。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int getv() {
int ans = 0, x;
read(x), ans += x;
read(x), ans += x;
read(x), ans += x;
read(x), ans += x;
return ans;
}
int main() {
int n, ans = 1;
read(n); n--;
int now = getv();
while (n--) {
int tmp = getv();
if (tmp > now) ans++;
}
writeln(ans);
return 0;
}
**【B】**The Bits
【思路要点】
- 令被交换的两个位置为 和 。
- 显然若要使得结果发生变化,必须要有 。
- 并且 中至少有一个不为 。
- 那么 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
char s[MAXN], t[MAXN];
long long one, zero, oneone, zeroone;
int main() {
int n; read(n);
scanf("\n%s", s + 1);
scanf("\n%s", t + 1);
for (int i = 1; i <= n; i++) {
if (s[i] == '1') {
one++;
if (t[i] == '1') oneone++;
} else {
zero++;
if (t[i] == '1') zeroone++;
}
}
writeln(one * zero - oneone * zeroone);
return 0;
}
**【C】**The Phone Number
【思路要点】
考虑枚举 的长度 ,我们可以通过如下方式得到一个 长度为 的序列:
将 分成 段,每段长度不超过 ,将每一段数字翻转。
枚举找到使得 最小的 即可。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int a[MAXN];
int main() {
int n; read(n);
int ans = 1e9, x = 0;
for (int i = 1; i <= n; i++) {
int y = n / i + (n % i != 0);
if (i + y < ans) {
ans = i + y;
x = i;
}
a[i] = i;
}
for (int i = 1; i <= n; i += x) {
int j = min(i + x - 1, n);
reverse(a + i, a + j + 1);
}
for (int i = 1; i <= n; i++)
printf("%d ", a[i]);
return 0;
}
**【D】**The Wu
【思路要点】
- 注意到 和 均很小,预处理出所有可能的询问的答案,询问时 回答即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 15;
const int MAXK = 105;
const int MAXS = 5005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, q, m, goal, w[MAXN], bit[MAXN];
int val[MAXS], cnt[MAXS], ans[MAXS][MAXK];
int calc(int x, int y) {
int tmp = goal ^ (x ^ y);
return val[tmp];
}
int getin() {
static char s[MAXN];
scanf("\n%s", s + 1);
int ans = 0;
for (int i = 1; i <= n; i++)
if (s[i] == '1') ans += bit[i];
return ans;
}
int main() {
read(n), read(m), read(q);
for (int i = 1; i <= n; i++) {
bit[i] = 1 << (i - 1);
read(w[i]);
}
goal = (1 << n) - 1;
for (int s = 0; s <= goal; s++) {
val[s] = 0;
for (int i = 1; i <= n; i++)
if (bit[i] & s) val[s] += w[i];
}
for (int i = 1; i <= m; i++) {
int x = getin();
cnt[x]++;
}
for (int s = 0; s <= goal; s++) {
for (int t = 0; t <= goal; t++)
if (calc(s, t) <= 100) ans[s][calc(s, t)] += cnt[t];
for (int i = 1; i <= 100; i++)
ans[s][i] += ans[s][i - 1];
}
while (q--) {
int s = getin(), k;
read(k);
writeln(ans[s][k]);
}
return 0;
}
**【E】**The Supersonic Rocket
【思路要点】
问题实际上要我们判断两个点集的凸包能否经过平移和旋转后重合。
令两个点集的凸包为 ,若 ,显然不可能重合。
对于每一个凸包(不妨为 ),取几何中心 (或凸包顶点坐标的平均值)
构造这样一个序列:
令 表示 倍长后的序列,那么两个凸包同构当且仅当 在 中出现过。
用 算法判断即可。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 4e5 + 5;
const long double eps = 1e-9;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct point {int x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; }
bool operator < (point a, point b) {
if (a.y == b.y) return a.x < b.x;
else return a.y < b.y;
}
long long dist(point a) {return 1ll * a.x * a.x + 1ll * a.y * a.y; }
int n, m, q;
point a[MAXN], b[MAXN], fp;
long double val[MAXN], vbl[MAXN];
bool solve() {
if (n != m) return false;
n--, m--;
long double px = 0, py = 0;
for (int i = 1; i <= n; i++) {
px += a[i].x;
py += a[i].y;
}
px /= n, py /= n;
for (int i = 1; i <= n; i++) {
val[2 * i - 1] = sqrt((px - a[i].x) * (px - a[i].x) + (py - a[i].y) * (py - a[i].y));
val[2 * i] = sqrt(1ll * (a[i + 1].x - a[i].x) * (a[i + 1].x - a[i].x) + 1ll * (a[i + 1].y - a[i].y) * (a[i + 1].y - a[i].y));
}
for (int i = 1; i <= 2 * n; i++)
val[i + 2 * n] = val[i];
px = 0, py = 0;
for (int i = 1; i <= m; i++) {
px += b[i].x;
py += b[i].y;
}
px /= m, py /= m;
for (int i = 1; i <= m; i++) {
vbl[2 * i - 1] = sqrt((px - b[i].x) * (px - b[i].x) + (py - b[i].y) * (py - b[i].y));
vbl[2 * i] = sqrt(1ll * (b[i + 1].x - b[i].x) * (b[i + 1].x - b[i].x) + 1ll * (b[i + 1].y - b[i].y) * (b[i + 1].y - b[i].y));
}
int la = n * 4;
int lb = m * 2;
static int nxt[MAXN];
for (int i = 2; i <= lb; i++) {
int p = nxt[i - 1];
while (p && fabs(vbl[p + 1] - vbl[i]) > eps)
p = nxt[p];
if (fabs(vbl[p + 1] - vbl[i]) <= eps) p++;
nxt[i] = p;
}
int pos = 0, ans = 0;
for (int i = 1; i <= la; i++) {
if (fabs(val[i] - vbl[pos + 1]) <= eps) {
pos++;
if (pos == lb) {
ans++;
pos = nxt[lb];
}
} else {
while (pos && fabs(val[i] - vbl[pos + 1]) > eps)
pos = nxt[pos];
if (fabs(val[i] - vbl[pos + 1]) <= eps) pos++;
}
}
return ans != 0;
}
bool cmp(point a, point b) {
long long tmp = (a - fp) * (b - fp);
if (tmp == 0) return dist(a - fp) < dist(b - fp);
else return tmp > 0;
}
bool online(point *a, int n) {
point tmp = a[n] - a[1];
for (int i = 2; i <= n - 1; i++)
if (tmp * (a[i] - a[1]) != 0) return false;
return true;
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
read(a[i].x), read(a[i].y);
for (int i = 2; i <= n; i++)
if (a[i] < a[1]) swap(a[i], a[1]);
fp = a[1];
sort(a + 2, a + n + 1, cmp);
for (int i = 1; i <= m; i++)
read(b[i].x), read(b[i].y);
for (int i = 2; i <= m; i++)
if (b[i] < b[1]) swap(b[i], b[1]);
fp = b[1];
sort(b + 2, b + m + 1, cmp);
if (online(a, n) && online(b, m)) {
if (dist(a[n] - a[1]) == dist(b[m] - b[1])) printf("YES\n");
else printf("NO\n");
return 0;
}
a[++n] = a[1];
int top = 1;
for (int i = 2; i <= n; i++) {
while (top >= 2 && (a[top] - a[top - 1]) * (a[i] - a[top - 1]) <= 0) top--;
a[++top] = a[i];
}
n = top;
b[++m] = b[1]; top = 1;
for (int i = 2; i <= m; i++) {
while (top >= 2 && (b[top] - b[top - 1]) * (b[i] - b[top - 1]) <= 0) top--;
b[++top] = b[i];
}
m = top;
if (solve()) printf("YES\n");
else printf("NO\n");
return 0;
}
**【F】**The Neutral Zone
【思路要点】
- 显然 。
- 对于小于等于 的 ,我们直接采用上式计算。
- 我们剩余的工作是计算 。
- 对 进行整数分块,用 筛计算每一块的质数 次幂和即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
unsigned n, A, B, C, D, limit;
unsigned f[MAXN], tot, prime[MAXN], sum1[MAXN], sum2[MAXN], sum3[MAXN];
unsigned m, x[MAXN], id1[MAXN], id2[MAXN], ans0[MAXN], ans1[MAXN], ans2[MAXN], ans3[MAXN];
void init(unsigned n) {
for (unsigned i = 2; i <= n; i++) {
if (f[i] == 0) {
f[i] = i;
prime[++tot] = i;
sum1[tot] = sum1[tot - 1] + i;
sum2[tot] = sum2[tot - 1] + i * i;
sum3[tot] = sum3[tot - 1] + i * i * i;
}
for (unsigned j = 1; j <= tot && prime[j] <= f[i]; j++){
unsigned tmp = prime[j] * i;
if (tmp > n) break;
f[tmp] = prime[j];
}
}
}
unsigned get2(unsigned x) {
unsigned a = x;
unsigned b = x + 1;
unsigned c = 2 * x + 1;
if (a % 2 == 0) a /= 2;
else if (b % 2 == 0) b /= 2;
else c /= 2;
if (a % 3 == 0) a /= 3;
else if (b % 3 == 0) b /= 3;
else c /= 3;
return a * b * c;
}
unsigned get3(unsigned x) {
unsigned a = x;
unsigned b = x + 1;
if (a % 2 == 0) a /= 2;
else b /= 2;
return a * b * a * b;
}
int main(){
read(n), read(A), read(B), read(C), read(D);
limit = sqrt(n); init(limit);
for (unsigned i = 1, j; i <= n; i = j + 1){
j = n / (n / i); x[++m] = n / i;
if (x[m] <= limit) id1[x[m]] = m;
else id2[n / x[m]] = m;
ans0[m] = x[m] - 1;
if ((x[m] + 2) % 2 == 0) ans1[m] = ((x[m] + 2) / 2) * (x[m] - 1);
else ans1[m] = (x[m] + 2) * ((x[m] - 1) / 2);
ans2[m] = get2(x[m]) - 1;
ans3[m] = get3(x[m]) - 1;
}
for (unsigned j = 1; j <= tot; j++)
for (unsigned i = 1; i <= m && prime[j] * prime[j] <= x[i]; i++) {
unsigned k = (x[i] / prime[j] <= limit) ? id1[x[i] / prime[j]] : id2[n / (x[i] / prime[j])];
ans3[i] = ans3[i] - prime[j] * prime[j] * prime[j] * (ans3[k] - sum3[j - 1]);
ans2[i] = ans2[i] - prime[j] * prime[j] * (ans2[k] - sum2[j - 1]);
ans1[i] = ans1[i] - prime[j] * (ans1[k] - sum1[j - 1]);
ans0[i] = ans0[i] - ans0[k] + j - 1;
}
unsigned ans = 0;
for (int i = 1; i <= tot; i++) {
unsigned cnt = 0;
unsigned tmp = n;
while (tmp >= prime[i]) {
cnt += tmp / prime[i];
tmp /= prime[i];
}
ans += cnt * (A * prime[i] * prime[i] * prime[i] + B * prime[i] * prime[i] + C * prime[i] + D);
}
for (unsigned i = 1; x[i] > limit; i++) {
ans += i * (ans3[i] - ans3[i + 1]) * A;
ans += i * (ans2[i] - ans2[i + 1]) * B;
ans += i * (ans1[i] - ans1[i + 1]) * C;
ans += i * (ans0[i] - ans0[i + 1]) * D;
}
writeln(ans);
return 0;
}
**【G】**The Tree
【思路要点】
记 表示作用在节点 上的 号操作的次数, 表示直接作用在节点 上的 号操作的次数,有 。
对于 号操作,求出 的值,若 ,点 为黑,否则为白。
我们发现每一个点 的 可以写成一个关于 的形如 的函数,并且令 有
。
即这样的函数的作用效果是可以合并的。
我们用树链剖分+线段树来维护这些函数,线段树上每一个节点代表了子树内所有函数依次作用的效果总和,这样就能够在 的时间内查询根节点到某一节点的函数依次作用的效果总和。
对于 号操作,将节点 的 均加一。
对于 号操作,先求出 ,将节点 的 均减去 ,以去除 及其祖先对于 子树内的影响,然后通过线段树的区间操作将 子树内除节点 以外的其余点的 还原为 ,以移除子树内的操作效果。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct info {int a, b; };
info operator + (info x, info y) {
x.a += y.a;
x.b += y.a;
chkmax(x.b, y.b);
return x;
}
struct SegmentTree {
struct Node {
int lc, rc;
info ans;
bool tag;
} a[MAXN * 2];
int n, root, size;
void update(int root) {
a[root].ans = a[a[root].lc].ans + a[a[root].rc].ans;
}
void build(int &root, int l, int r) {
root = ++size;
if (l == r) {
a[root].ans = (info) {-1, 0};
return;
}
int mid = (l + r) / 2;
build(a[root].lc, l, mid);
build(a[root].rc, mid + 1, r);
update(root);
}
void init(int x) {
n = x;
root = size = 0;
build(root, 1, n);
}
void pushdown(int root, int l, int r) {
if (a[root].tag) {
int mid = (l + r) / 2;
a[a[root].lc].ans = (info) {-(mid - l + 1), 0};
a[a[root].lc].tag = true;
a[a[root].rc].ans = (info) {-(r - mid), 0};
a[a[root].rc].tag = true;
a[root].tag = false;
}
}
void inc(int root, int l, int r, int pos) {
if (l == r) {
a[root].ans.a++;
a[root].ans.b++;
return;
}
pushdown(root, l, r);
int mid = (l + r) / 2;
if (mid >= pos) inc(a[root].lc, l, mid, pos);
else inc(a[root].rc, mid + 1, r, pos);
update(root);
}
void inc(int pos) {
inc(root, 1, n, pos);
}
void dec(int root, int l, int r, int pos, int delta) {
if (l == r) {
a[root].ans.a -= delta;
a[root].ans.b -= delta;
return;
}
pushdown(root, l, r);
int mid = (l + r) / 2;
if (mid >= pos) dec(a[root].lc, l, mid, pos, delta);
else dec(a[root].rc, mid + 1, r, pos, delta);
update(root);
}
void dec(int pos, int delta) {
dec(root, 1, n, pos, delta);
}
info query(int root, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return a[root].ans;
pushdown(root, l, r);
int mid = (l + r) / 2;
if (mid >= qr) return query(a[root].lc, l, mid, ql, qr);
else if (mid + 1 <= ql) return query(a[root].rc, mid + 1, r, ql, qr);
else return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr);
}
info query(int l, int r) {
return query(root, 1, n, l, r);
}
void clear(int root, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
a[root].tag = true;
a[root].ans = (info) {-(r - l + 1), 0};
return;
}
pushdown(root, l, r);
int mid = (l + r) / 2;
if (mid >= ql) clear(a[root].lc, l, mid, ql, min(qr, mid));
if (mid + 1 <= qr) clear(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
update(root);
}
void clear(int l, int r) {
clear(root, 1, n, l, r);
}
} ST;
int n, m;
int size[MAXN], depth[MAXN];
int timer, dfn[MAXN], rit[MAXN];
int son[MAXN], up[MAXN], father[MAXN];
vector <int> a[MAXN];
void dfs(int pos) {
size[pos] = 1, son[pos] = 0;
for (unsigned i = 0; i < a[pos].size(); i++) {
int tmp = a[pos][i];
depth[tmp] = depth[pos] + 1;
father[tmp] = pos;
dfs(tmp);
size[pos] += size[tmp];
if (size[tmp] > size[son[pos]]) son[pos] = tmp;
}
}
void efs(int pos, int from) {
dfn[pos] = ++timer;
up[pos] = from;
if (son[pos]) efs(son[pos], from);
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i] != son[pos]) efs(a[pos][i], a[pos][i]);
rit[pos] = timer;
}
int query(int pos) {
info ans = ST.query(dfn[up[pos]], dfn[pos]);
pos = father[up[pos]];
while (pos != 0) {
ans = ST.query(dfn[up[pos]], dfn[pos]) + ans;
pos = father[up[pos]];
}
return max(ans.a, ans.b);
}
void modify(int pos) {
ST.dec(dfn[pos], query(pos));
if (dfn[pos] != rit[pos]) ST.clear(dfn[pos] + 1, rit[pos]);
}
int main() {
read(n), read(m);
for (int i = 2; i <= n; i++) {
int x; read(x);
a[x].push_back(i);
}
dfs(1);
efs(1, 1);
ST.init(n);
for (int i = 1; i <= m; i++) {
int type, pos;
read(type), read(pos);
if (type == 1) ST.inc(dfn[pos]);
if (type == 2) modify(pos);
if (type == 3) {
int tmp = query(pos);
if (tmp != 0) printf("black\n");
else printf("white\n");
}
}
return 0;
}
**【H】**The Films
【思路要点】
不难发现答案为
其中 , 表示 在序列中出现的次数, 表示 在给定区间中出现的次数。
对于每个不同的 ,分别用莫队算法计算答案即可。
当序列长度为 ,询问个数为 ,分块大小为 ,莫队算法的时间复杂度为 。
取 可以得到 的时间复杂度。
令 为 的不同取值的个数,上述算法复杂度瓶颈在于对于每个取值,询问个数相差较小的情况,即最坏时间复杂度为 。
时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int P = 998244353;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct querys {int l, r, k, home; } qry[MAXN];
int prod[MAXN], inv[MAXN];
int n, m, q, size, blocks, block[MAXN];
int now, nowk, a[MAXN], b[MAXN], e[MAXN], ans[MAXN];
bool cmp(querys a, querys b) {
return a.k < b.k;
}
bool cnp(querys a, querys b) {
if (block[a.l] == block[b.l]) return a.r < b.r;
else return block[a.l] < block[b.l];
}
int power(int x, int y) {
if (y == 0) return 1;
int tmp = power(x, y / 2);
if (y % 2 == 0) return 1ll * tmp * tmp % P;
else return 1ll * tmp * tmp % P * x % P;
}
void add(int col) {
b[col]++;
int tmp = a[col] + nowk - b[col] + 1;
now = 1ll * now * tmp % P;
}
void del(int col) {
int tmp = a[col] + nowk - b[col] + 1;
now = 1ll * now * inv[tmp] % P;
b[col]--;
}
int main() {
read(n), read(m), read(q);
for (int i = 1; i <= n; i++)
read(e[i]), a[e[i]]++;
for (int i = 1; i <= q; i++)
read(qry[i].l), read(qry[i].r), read(qry[i].k), qry[i].home = i;
for (int i = 1; i < MAXN; i++)
inv[i] = power(i, P - 2);
sort(qry + 1, qry + q + 1, cmp);
for (int i = 1, nxt; i <= q; i = nxt) {
nxt = i; while (nxt <= q && qry[nxt].k == qry[i].k) nxt++;
int cntq = nxt - i, tmp = 1ll * qry[i].k * m % P;
size = max(n / sqrt(cntq), 1.0), blocks = 0;
prod[0] = 1;
for (int j = 1; j <= n; j++) {
prod[j] = 1ll * prod[j - 1] * (tmp + j) % P;
if (j % size == 1 % size) blocks++;
block[j] = blocks;
}
sort(qry + i, qry + nxt, cnp);
int l = qry[i].l, r = qry[i].l - 1;
now = 1, nowk = qry[i].k;
memset(b, 0, sizeof(b));
for (int j = i; j < nxt; j++) {
while (r < qry[j].r) add(e[++r]);
while (l > qry[j].l) add(e[--l]);
while (r > qry[j].r) del(e[r--]);
while (l < qry[j].l) del(e[l++]);
ans[qry[j].home] = 1ll * now * prod[n - (qry[j].r - qry[j].l + 1)] % P;
}
}
for (int i = 1; i <= q; i++)
writeln(ans[i]);
return 0;
}