题意:初始化下,给你1到n的n个数字。接下来进行m次操作,每次操作有两种类型,操作1:将求区间[l,r]之间的所有与p互质的数的和,操作2:将x位置上的数改成c。
思路:直接求与p互质的数的和不好求,可以求出所有与p不互质的数的和再用区间和减去这个数。
然后就对p进行质因子分解,并用二进制枚举每次每种可能的与p的公因数,并用容斥的思想和等差数列求和记录每个公因数的在[l,r]范围内贡献。
对于操作2就很好处理了,每次进行操作1时,对所有在[l,r]范围内的修改进行判断加上与当前查询p互质的修改的值,如果被修改前的值也是与p互质的,则要减去。
map<int, int> mp;
vector<ll> v;
void prime(ll p)
{
v.clear();
for (ll i = 2;i*i<= p;i++)
{
if (p%i == 0)
{
while (p%i == 0)
p /= i;
v.emplace_back(i);
}
}
if (p > 1)v.emplace_back(p);
}
ll sum(ll l, ll r, ll c)
{
ll st, ed, num;
st = (ll)ceil(l*1.0 / c)*c;
ed = r / c*c;
num = r / c - (l - 1) / c;
return st > ed ? 0 : num * (st + ed) / 2;
}
ll get(ll l,ll r)
{
int n = v.size();
ll ans = 0;
f(i, 1, (1 << n) - 1)
{
int cot = 0;
ll tmp = 1;
f(j, 0, n - 1)
{
if (i>>j&1)
{
cot++;
tmp *= v[j];
}
}
if (cot & 1)ans += sum(l, r, tmp);
else ans -= sum(l, r, tmp);
}
return ans;
}
int main()
{
//freopen("in.txt", "r", stdin);
int t;
cin >> t;
while (t--)
{
mp.clear();
int n, m;
scanf("%d%d", &n, &m);
ll x, y, op, c;
while (m--)
{
scanf("%lld", &op);
if (op == 1)
{
scanf("%lld%lld%lld", &x, &y, &c);
if (x > y)swap(x, y);
prime(c);
ll ans = (ll)(y - x + 1)*(x + y) / 2;
ans -= get(x, y);
for (auto i : mp)
{
if (i.first >= x && i.first <= y)
{
if (gcd(i.second, c) == 1)ans += i.second;
if (gcd(i.first, c) == 1)ans -= i.first;
}
}
printf("%lld\n", ans);
}
else
{
scanf("%lld%lld", &x, &c);
mp[x] = c;
}
}
}
return 0;
}