結果
| 問題 |
No.1891 Static Xor Range Composite Query
|
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:01:28 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 723 ms / 5,000 ms |
| コード長 | 9,057 bytes |
| コンパイル時間 | 915 ms |
| コンパイル使用メモリ | 96,536 KB |
| 実行使用メモリ | 81,228 KB |
| 最終ジャッジ日時 | 2025-05-14 13:03:53 |
| 合計ジャッジ時間 | 13,581 ms |
|
ジャッジサーバーID (参考情報) |
judge3 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 1 |
| other | AC * 30 |
ソースコード
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm> // For std::min, std::max
// Use long long for indices and intermediate calculations
using ll = long long;
ll N; // Length of the sequence of functions. Assumed to be power of 2.
int LOGN; // log2(N)
const ll MOD = 998244353; // Modulus for calculations
// Helper function for modular addition
ll add_mod(ll a, ll b) {
ll res = a + b;
// Adjust result to be within [0, MOD-1]
if (res >= MOD) res -= MOD;
if (res < 0) res += MOD;
return res;
}
// Helper function for modular multiplication
ll mul_mod(ll a, ll b) {
ll res = (a * b) % MOD;
// Adjust result to be within [0, MOD-1]
if (res < 0) res += MOD;
return res;
}
// Structure to represent a linear function f(x) = ax + b
struct LinearFunc {
ll a, b; // Coefficients a and b
// Default constructor for identity function f(x) = 1*x + 0
LinearFunc(ll _a = 1, ll _b = 0) : a(_a), b(_b) {}
// Computes the composition (this o other)(x) = this(other(x))
// Composition order convention: If F = f2 o f1, then F(x) = f2(f1(x)).
// In segment tree context, if node combines Left and Right children,
// Parent = Right.compose(Left) corresponds to applying Left then Right.
LinearFunc compose(const LinearFunc& other) const {
// this(x) = a*x + b
// other(x) = other.a*x + other.b
// this(other(x)) = a * (other.a * x + other.b) + b
// = (a * other.a) * x + (a * other.b + b)
ll new_a = mul_mod(a, other.a);
ll new_b = add_mod(mul_mod(a, other.b), b);
return LinearFunc(new_a, new_b);
}
// Apply the linear function to a value x
ll apply(ll x) const {
ll res = add_mod(mul_mod(a, x), b);
return res;
}
};
// Precomputed table storing composite functions
// Comp[s][idx] stores the composite function related to level 's' and index 'idx'.
// The specific meaning of idx depends on the precomputation logic derived from FWHT-like structure.
std::vector<std::vector<LinearFunc>> Comp;
// Precomputation function to fill the Comp table
// This function computes composite functions for all dyadic intervals and all relevant XOR parameters ('p_low')
// Time complexity: O(N log N)
void precompute() {
// Iterate through levels of the segment tree / decomposition structure, from s=1 up to LOGN
for (int s = 1; s <= LOGN; ++s) {
// level_len is the size of intervals at the previous level (s-1)
ll level_len = 1LL << (s - 1);
// current_len is the size of intervals at the current level (s)
ll current_len = 1LL << s;
// Number of blocks of size current_len in the range [0, N)
ll num_blocks = N / current_len;
// Iterate over each block k at the current level s
for (ll k = 0; k < num_blocks; ++k) {
// Iterate over all possible values of p_low, which has s bits
for (ll p_low = 0; p_low < current_len; ++p_low) {
// Extract the highest bit of p_low (the bit at position s-1)
ll p_high_bit = (p_low >> (s - 1)) & 1;
// Extract the lower s-1 bits of p_low
// The mask `level_len - 1` gives a mask with s-1 ones.
ll p_prime_low = p_low & (level_len - 1);
// Indices of the two child blocks at level s-1
ll kL = k * 2; // Left child block index
ll kR = k * 2 + 1; // Right child block index
// Calculate the indices into Comp[s-1] table for the children results
// These indices depend on child block index (kL/kR) and the lower part of p (p_prime_low)
ll idxL = kL * level_len + p_prime_low;
ll idxR = kR * level_len + p_prime_low;
// Retrieve precomputed functions for children intervals
LinearFunc CompL = Comp[s - 1][idxL];
LinearFunc CompR = Comp[s - 1][idxR];
// Calculate the index into Comp[s] table for the current state (k, p_low)
ll current_idx = k * current_len + p_low;
// Combine the results based on the highest bit of p_low (p_high_bit)
// This corresponds to the butterfly operations in FWHT-like transforms.
if (p_high_bit == 0) {
// If highest bit is 0, XOR doesn't swap halves. The standard order of composition applies:
// The function for the right interval is applied *after* the function for the left interval.
Comp[s][current_idx] = CompR.compose(CompL);
} else {
// If highest bit is 1, XOR effectively swaps the roles of the left and right halves.
// The function for the left interval is applied *after* the function for the right interval.
Comp[s][current_idx] = CompL.compose(CompR);
}
}
}
}
}
// Recursive query function to compute the composite function for range [ql, qr) with XOR parameter p
// Uses standard segment tree query logic, fetching precomputed values for full dyadic interval overlaps.
// Time complexity: O(log N) per query
LinearFunc query_recursive(ll L, ll R, ll ql, ll qr, ll p) {
// Base case 1: Query range [ql, qr) does not overlap with node range [L, R)
if (qr <= L || R <= ql) {
return LinearFunc(); // Return identity function (1, 0)
}
// Base case 2: Query range fully covers node range [L, R)
if (ql <= L && R <= qr) {
ll len = R - L; // Length of the interval [L, R)
if (len == 0) return LinearFunc(); // Empty interval case
// Check if [L, R) is a dyadic interval (length is power of 2) aligned at block boundary (L is multiple of len)
// Bit trick `(len & (len - 1)) == 0` checks if len is power of 2 (and len > 0).
if (len > 0 && (len & (len - 1)) == 0 && (L % len == 0)) {
// Find the level 's' such that 2^s = len
int s = __builtin_ctzll(len); // Count trailing zeros gives log2 for powers of 2
ll k = L / len; // block index k for interval [L, R)
ll p_high = p >> s; // Upper bits of p (relevant for block XORing)
ll p_low = p & ((1LL << s) - 1); // Lower s bits of p (relevant within block)
ll k_prime = k ^ p_high; // Effective block index after XORing with p_high
ll idx = k_prime * len + p_low; // Calculate index in the precomputed table Comp[s]
// Basic boundary check (optional, but good practice)
if (s >= 0 && s <= LOGN && idx >= 0 && idx < N) {
return Comp[s][idx]; // Return the precomputed result
} else {
// Should not happen with correct logic and constraints. Return identity as safe fallback.
return LinearFunc();
}
}
// If not a perfectly aligned dyadic block covered by query, proceed with recursion.
}
// Recursive step: Split interval [L, R) into [L, M) and [M, R)
ll M = L + (R - L) / 2; // Midpoint
// Recursively query left and right children
LinearFunc resL = query_recursive(L, M, ql, qr, p);
LinearFunc resR = query_recursive(M, R, ql, qr, p);
// Combine results: Function for Right interval is applied AFTER function for Left interval.
// This matches the problem statement: f_{r-1} o ... o f_l
return resR.compose(resL);
}
int main() {
// Faster I/O
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int Q; // Number of queries
std::cin >> N >> Q;
// Calculate LOGN = log2(N) since N is guaranteed power of 2
LOGN = 0;
if (N > 0) {
ll tempN = N;
while(tempN > 1) {
tempN >>= 1;
LOGN++;
}
}
// Initialize the Comp table structure
Comp.resize(LOGN + 1);
for (int s = 0; s <= LOGN; ++s) {
Comp[s].resize(N);
}
// Read the initial N linear functions f_i(x) = a_i * x + b_i
// Store them in the base level Comp[0]
// Comp[0][i] represents the function f_i
for (ll i = 0; i < N; ++i) {
ll a, b;
std::cin >> a >> b;
Comp[0][i] = LinearFunc(a, b);
}
// Perform the precomputation O(N log N)
precompute();
// Process Q queries
for (int i = 0; i < Q; ++i) {
ll l, r, p, x; // Query parameters: range [l, r), XOR value p, initial value x
std::cin >> l >> r >> p >> x;
// Compute the composite function for the query range [l, r) with XOR p
// Query range is [l, r), representing indices l, l+1, ..., r-1
LinearFunc final_func = query_recursive(0, N, l, r, p);
// Apply the final composite function to the initial value x
ll result = final_func.apply(x);
// Output the result
std::cout << result << "\n";
}
return 0;
}
qwewe