2024 CCPC 郑州 G - Same Sum
神秘题目,感觉是某一类典题。
考虑维护一个区间的怎么匹配,很显然是头尾匹配,否则会形成偏序关系导致大小不等。
题解很神秘的掏出了一个哈希函数
\[F_{+}(l, \, r) = \sum_{i = l}^r x^{a_i}
\]
\[F_{-}(l, \, r) = \sum_{i = l}^r x^{-a_i}
\]
其中 \(x\) 为任意哈希值,如果一个区间能够匹配,有 \(F_{+}(l, \, r) = x^{2m}F_{-}(l, \, r)\),\(m\) 为区间平均值。
这是直观的,就和回文串哈希相等类似,我们可以把区间平均值拍到 \(0\) 上,然后 \(F_{+}\) 就是 \(\Delta > 0\),\(F_{-}\) 就是 \(\Delta < 0\),拍到 \(0\) 上必然有正负匹配,那么转移回来就相当于两者差了两倍 \(\Delta\)。
用双哈希可以草过此题,时间复杂度 \(O(n\log{n}\log{V})\)。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<ll, ll> PII;
typedef pair<ll, PII> PPI;
const int N = 2e5 + 10, p1 = 1e9 + 7, p2 = 1e9 + 9;
const ll b1 = 131, b2 = 133;
int n, q, a[N];
struct Node {
int l, r;
ll sum, add;
}tr[N << 2], pos1[N << 2], neg1[N << 2], pos2[N << 2], neg2[N << 2];
ll add(ll a, ll b, ll mod) {
if (!mod) return a + b;
return (a + b < mod ? a + b : a + b - mod);
}
ll ksm(ll a, ll k, ll mod) {
ll res = 1;
a %= mod;
while (k) {
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
}
return res;
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
pos1[u].sum = add(pos1[u << 1].sum, pos1[u << 1 | 1].sum, p1);
pos2[u].sum = add(pos2[u << 1].sum, pos2[u << 1 | 1].sum, p2);
neg1[u].sum = add(neg1[u << 1].sum, neg1[u << 1 | 1].sum, p1);
neg2[u].sum = add(neg2[u << 1].sum, neg2[u << 1 | 1].sum, p2);
}
void pushdown(int u) {
if (tr[u].add) {
ll x = tr[u].add;
tr[u].add = 0, tr[u << 1].add += x, tr[u << 1 | 1].add += x;
tr[u << 1].sum += (tr[u << 1].r - tr[u << 1].l + 1) * x;
tr[u << 1 | 1].sum += (tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * x;
ll pw1 = ksm(b1, x, p1), pw2 = ksm(b2, x, p2);
ll inv1 = ksm(pw1, p1 - 2, p1), inv2 = ksm(pw2, p2 - 2, p2);
(pos1[u << 1].sum *= pw1) %= p1, (pos1[u << 1 | 1].sum *= pw1) %= p1;
(pos2[u << 1].sum *= pw2) %= p2, (pos2[u << 1 | 1].sum *= pw2) %= p2;
(neg1[u << 1].sum *= inv1) %= p1, (neg1[u << 1 | 1].sum *= inv1) %= p1;
(neg2[u << 1].sum *= inv2) %= p2, (neg2[u << 1 | 1].sum *= inv2) %= p2;
}
}
void build(int u, int l, int r) {
tr[u] = pos1[u] = pos2[u] = neg1[u] = neg2[u] = {l, r};
if (l == r) {
tr[u].sum = a[l];
pos1[u].sum = ksm(b1, a[l], p1);
pos2[u].sum = ksm(b2, a[l], p2);
neg1[u].sum = ksm(pos1[u].sum, p1 - 2, p1);
neg2[u].sum = ksm(pos2[u].sum, p2 - 2, p2);
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, ll x) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].sum += (tr[u].r - tr[u].l + 1) * x, tr[u].add += x;
ll pw1 = ksm(b1, x, p1), pw2 = ksm(b2, x, p2);
ll inv1 = ksm(pw1, p1 - 2, p1), inv2 = ksm(pw2, p2 - 2, p2);
(pos1[u].sum *= pw1) %= p1, (pos2[u].sum *= pw2) %= p2;
(neg1[u].sum *= inv1) %= p1, (neg2[u].sum *= inv2) %= p2;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, x);
if (r > mid) modify(u << 1 | 1, l, r, x);
pushup(u);
}
ll query_sum(Node *tr, int u, int l, int r, int mod) {
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res = query_sum(tr, u << 1, l, r, mod);
if (r > mid) res = add(res, query_sum(tr, u << 1 | 1, l, r, mod), mod);
return res;
}
string query(int l, int r) {
ll sum = query_sum(tr, 1, l, r, 0);
if (sum % (r - l + 1 >> 1)) return "NO";
sum /= (r - l + 1 >> 1);
if (query_sum(pos1, 1, l, r, p1) != query_sum(neg1, 1, l, r, p1) * ksm(b1, sum, p1) % p1) return "NO";
if (query_sum(pos2, 1, l, r, p2) != query_sum(neg2, 1, l, r, p2) * ksm(b2, sum, p2) % p2) return "NO";
return "YES";
}
void solve() {
cin >> n >> q;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
build(1, 1, n);
for (int i = 1; i <= q; i ++ ) {
int op, l, r, x;
cin >> op >> l >> r;
if (op == 1) cin >> x, modify(1, l, r, x);
else cout << query(l, r) << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}