时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
给一个数列共n(n<=100,000)个数,a1,a2,...,an.(0<=ai<=1000,000,000).有q(q<=100,000)个询问。每个询问为l,r(1<=l<=r<=n).求gcd(al,al+1,...,ar).
再求区间[l,r]的子区间中(l<=l'<=r'<=r)满足gcd(al,al+1,...,ar) = gcd(al',al'+1,...ar')的子区间个数.
再求区间[l,r]的子区间中(l<=l'<=r'<=r)满足gcd(al,al+1,...,ar) = gcd(al',al'+1,...ar')的子区间个数.
输入描述:
第一行一个数T表示数据组数 第二行一个数n 接下来一行n个数,a1,a2,...,an 接下一行一个数q 接下来一行2个数l和r。
输出描述:
首先输出一行“Case #:t”,t代表当前是第几组数据。 接下来q行,每行输出2个数,第一个是gcd(al,al+1,...,ar), 第二个是区间[l,r]的子区间中(l<=l'<=r'<=r)满足gcd(al,al+1,...,ar) = gcd(al',al'+1,...ar')的子区间个数。
示例1
输入
2 5 1 2 4 6 7 4 1 5 2 4 3 4 4 4 5 1 2 4 2 1 6 1 1 1 3 2 2 2 3 2 4 3 3
输出
Case #1: 1 8 2 4 2 1 6 1 Case #2: 1 1 1 3 2 1 2 2 2 5 4 1
解题思路:
可以线段树暴力上,不过需要保存的东西比较多,需要保存区间的gcd,区间内和区间gcd不一样的子区间个数,区间长度,固定区间左端点向右拓展gcd的情况,固定区间右端点向左拓展gcd的情况
以每一个位置 i 作为区间右端点,左端点从[1, i] 移动的过程中,区间 gcd 的种类不会超过 log(n) 种,可以把这些处理出来,记录成四元组(L1, L2, R, g),表示区间左端点在[L1, L2],右端点在 R 的所有区间,最大公约数均为 g。所有的四元组已经包含了所有的子区间。那么对于每一次的询问(L, R, g),只需在和这一次询问拥有相同 g 的四元组中寻找答案,去计算每一个四元组做出的贡献,可以按 R 排序,然后利用线段树区间修改、求区间和来处理。
方法一:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;
LL a[100009];
int n, q, l, r;
int gcd(int a, int b)
{
return a ? gcd(b % a, a) : b;
}
struct node
{
vector<pair<int, int> >l, r;
LL cnt;
int len, x;
static void Merge(vector<pair<int, int> > &a, const vector<pair<int, int> >&b)
{
for (int i = 0; i < b.size(); i++)
{
pair<int, int>tmp = b[i];
if (tmp.first % a.back().first == 0) a.back().second += tmp.second;
else a.push_back(make_pair(gcd(tmp.first, a.back().first), tmp.second));
}
}
static LL solve(const vector<pair<int, int> > &a, const vector<pair<int, int> > &b, const int &x)
{
LL ans = 0;
int sum = 0, Size = b.size() - 1;
for (int i = 0; i < b.size(); i++) sum += b[i].second;
for (int i = 0; i < a.size(); i++)
{
pair<int, int>tmp = a[i];
while (Size >= 0 && gcd(tmp.first, b[Size].first) == x)
sum -= b[Size--].second;
ans += 1LL * tmp.second * sum;
}
return ans;
}
node operator +(const node &a)const
{
node ans;
ans.x = gcd(x, a.x);
ans.cnt = 0;
ans.cnt += x == ans.x ? cnt : 1LL * (len + 1) * len / 2;
ans.cnt += a.x == ans.x ? a.cnt : 1LL * (a.len + 1) * a.len / 2;
ans.len = len + a.len;
Merge(ans.l = l, a.l);
Merge(ans.r = a.r, r);
ans.cnt += solve(r, a.l, ans.x);
return ans;
}
}x[100009 << 2];
void build(int k, int l, int r)
{
if (l == r)
{
x[k].l.clear(), x[k].r.clear();
x[k].l.push_back(make_pair(a[l], 1));
x[k].r.push_back(make_pair(a[l], 1));
x[k].x = a[l];
x[k].cnt = (x[k].len = 1) - 1;
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
x[k] = x[k << 1] + x[k << 1 | 1];
}
node query(int k, int l, int r, int ll, int rr)
{
if (l >= ll && r <= rr) return x[k];
int mid = (l + r) >> 1;
if (mid >= rr) return query(k << 1, l, mid, ll, rr);
else if (mid < ll) return query(k << 1 | 1, mid + 1, r, ll, rr);
return query(k << 1, l, mid, ll, rr) + query(k << 1 | 1, mid + 1, r, ll, rr);
}
int main()
{
int t, cas = 0;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
build(1, 1, n);
scanf("%d", &q);
printf("Case #%d:\n", ++cas);
while (q--)
{
scanf("%d %d", &l, &r);
node ans = query(1, 1, n, l, r);
printf("%d %lld\n", ans.x, 1LL * ans.len * (ans.len + 1) / 2 - ans.cnt);
}
}
return 0;
}
方法二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;
int n, m, l, r;
int x[1000009 << 2], a[1000009];
LL lazy[1000009 << 2], sum[1000009 << 2];
struct node
{
int l, r, g, id;
LL ans;
bool operator < (const node &x)const
{
if (g != x.g) return g < x.g;
return r < x.r;
}
}b[1000009];
struct node1
{
int l1, l2, r, g;
bool operator < (const node1 &x)const
{
if (g != x.g) return g < x.g;
return r < x.r;
}
}c[1000009];
int gcd(int x, int y)
{
return x ? gcd(y % x, x) : y;
}
void build(int k, int l, int r)
{
if (l == r)
{
scanf("%d", &x[k]);
a[l] = x[k];
return;
}
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1 , mid + 1, r);
x[k] = gcd(x[k << 1], x[k << 1 | 1]);
}
int query(int k, int l, int r, int ll, int rr)
{
if (ll <= l && r <= rr) return x[k];
int mid = (l + r) >> 1;
if (rr <= mid) return query(k << 1, l, mid, ll, rr);
else if (ll > mid) return query(k << 1 | 1, mid + 1, r, ll, rr);
return gcd(query(k << 1, l, mid, ll, rr), query(k << 1 | 1, mid + 1, r, ll, rr));
}
void Push(int k, int l, int r)
{
int mid = (l + r) >> 1;
sum[k << 1] += (lazy[k] * (mid - l + 1));
sum[k << 1 | 1] += (lazy[k] * (r - mid));
lazy[k << 1] += lazy[k], lazy[k << 1 | 1] += lazy[k];
lazy[k] = 0;
}
void update(int k, int l, int r, int ll, int rr, LL val)
{
if (ll <= l && r <= rr)
{
sum[k] += (val * (r - l + 1));
lazy[k] += val;
return;
}
if (lazy[k]) Push(k, l, r);
int mid = (l + r) >> 1;
if (ll <= mid) update(k << 1, l, mid, ll, rr, val);
if (rr > mid) update(k << 1 | 1, mid + 1, r, ll, rr, val);
sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
LL get(int k, int l, int r, int ll, int rr)
{
if (ll <= l && r <= rr) return sum[k];
if (lazy[k]) Push(k, l, r);
int mid = (l + r) / 2;
LL ans = 0;
if (ll <= mid) ans += get(k << 1, l, mid, ll, rr);
if (rr > mid) ans += get(k << 1 | 1, mid + 1, r, ll, rr);
return ans;
}
bool cmp(const node &a, const node &b)
{
return a.id < b.id;
}
int main()
{
int t, cas = 0;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
build(1, 1, n);
scanf("%d", &m);
printf("Case #%d:\n", ++cas);
for (int i = 0; i < m; i++)
{
scanf("%d %d", &b[i].l, &b[i].r);
b[i].g = query(1, 1, n, b[i].l, b[i].r);
b[i].id = i;
}
int cnt = 0;
stack<pair<int, int> >s1, s2;
for (int i = 1; i <= n; i++)
{
s1.push(make_pair(a[i], 1));
while (!s1.empty()) s2.push(make_pair(gcd(s1.top().first, a[i]), s1.top().second)), s1.pop();
while (!s2.empty())
{
pair<int, int>pre = s2.top(); s2.pop();
if (!s1.empty() && pre.first == s1.top().first) pre.second += s1.top().second, s1.pop();
s1.push(pre);
}
int tmp = i;
while (!s1.empty())
{
pair<int, int>pre = s1.top(); s1.pop();
c[cnt] = { tmp - pre.second + 1, tmp, i, pre.first };
cnt++;
tmp = tmp - pre.second;
s2.push(pre);
}
while (!s2.empty())
{
s1.push(s2.top());
s2.pop();
}
}
sort(b, b + m);
sort(c, c + cnt);
for (int i = 0, j = 0; i < cnt; i = j + 1, j = i)
{
while (j < cnt - 1 && c[j].g == c[j + 1].g) j++;
int l = 0, r = m - 1, pos1 = -1, pos2 = -1;
while (l <= r)
{
int mid = (l + r) / 2;
if (b[mid].g < c[i].g) l = mid + 1;
else if (b[mid].g > c[i].g) r = mid - 1;
else { pos1 = mid; r = mid - 1; }
}
if (pos1 == -1) continue;
l = 0, r = m - 1;
while (l <= r)
{
int mid = (l + r) / 2;
if (b[mid].g < c[i].g) l = mid + 1;
else if (b[mid].g > c[i].g) r = mid - 1;
else { pos2 = mid; l = mid + 1; }
}
int tmp = i;
for (int k = pos1; k <= pos2; k++)
{
while (tmp <= j && c[tmp].r <= b[k].r)
{
update(1, 1, n, c[tmp].l1, c[tmp].l2, 1LL);
tmp++;
}
b[k].ans = get(1, 1, n, b[k].l, b[k].r);
}
for (int k = i; k < tmp; k++)
update(1, 1, n, c[k].l1, c[k].l2, -1LL);
}
sort(b, b + m, cmp);
for (int i = 0; i < m; i++)
printf("%d %lld\n", b[i].g, b[i].ans);
}
return 0;
}