描述
题解
这个题的题意有些繁琐,看了好久才看懂。
首先给定一个 1 n 序列,要你进行一系列变换,直到没有变化后,然后对该序列进行区间查询。
说起区间查询,很容易想到的就是线段树,可是这个题和线段树有一些差异,因为这个序列变化后是有规律的,划开奇偶看,分别是一个等差数列,所以我们需要处理出来等差数列的第一项就好了。剩下的区间查询就和线段树差不多了,不过不用建树,这倒是一个很有趣的变化。
代码
#include <cstdio>
#include <algorithm>
using namespace std;
long long n, m, mod;
long long l, r, u, v;
long long cal(long long a, long long b, long long c)
{
a -= b;
if (a < 0)
{
a = -1;
}
else
{
a = a >> c;
}
long long B = b + a * (1ll << c);
long long s;
if ((++a) & 1)
{
s = (((B + b) >> 1) % mod) * (a % mod);
}
else
{
s = ((B + b) % mod) * ((a >> 1) % mod);
}
return s % mod;
}
long long _cal(long long ll, long long rr, long long c, int d)
{
if (u > n)
{
return 0;
}
long long ans = 0;
if (l > ll || r < rr)
{
long long m = (ll + rr) >> 1;
if (l <= m)
{
ans += _cal(ll, m, c, d + 1);
}
if (r > m)
{
ans += _cal(m + 1, rr, c + (1ll << d), d + 1);
}
if (ans >= mod)
{
ans -= mod;
}
}
else
{
ans = cal(v, c, d) - cal(u - 1, c, d);
if (ans < 0)
{
ans += mod;
}
}
return ans;
}
int main()
{
scanf("%lld%lld%lld", &n, &m, &mod);
while (m--)
{
scanf("%lld%lld%lld%lld", &l, &r, &u, &v);
v = min(v, n);
long long ans = _cal(1, n, 1, 0);
printf("%lld\n", ans % mod);
}
return 0;
}