您的位置:首页 > 其它

HDU 5381 The sum of gcd (莫队 + 二分 + 区间GCD特性)

2018-03-14 10:32 369 查看
/**
题意:....

思路: 固定一个端点(值为k),那么这个端点属于的所有区间的不同的gcd最多只有logk中,因为gcd 整除 k, 从大到小排序去重后
后一个数至少除以2,所以不同的GCD最多只有logk个,那么找出这个端点往左和往右当GCD为g时能扩展到多远,这个二分即可,区间
的gcd用ST表预处理出来,之后找出关系式后莫队算法就可以了
*/

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e4 + 10;
using namespace std;

int n, m, T, kase = 1;
int a[maxn], Q, block[maxn];
int st[maxn][15];
ll ans[maxn];
struct qry {
int b, l, r, id;
qry() {}
qry(int b, int l, int r, int id) : b(b), l(l), r(r), id(id) {}
bool operator < (qry p) const {
if(b != p.b) return b < p.b;
return r < p.r;
}
} q[maxn];
struct P {
int l, r, g;
P() {}
P(int l, int r, int g) : l(l), r(r), g(g) {}
};
vector<P> gl[maxn], gr[maxn];
int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }

void init() {
for(int i = 1; (1 << i) <= n; i++) {
int sz = (1 << (i - 1)), s = (1 << i);
for(int x = 1; x < n; x++) {
if(x + s - 1 > n) break;
int j = x + sz;
st[x][i] = gcd(st[x][i - 1], st[j][i - 1]);
}
}
}

int query(int l, int r, int val) {
int tot = (r - l + 1), i = 0;
while((1 << i) <= tot) i++; i--;
for( ; i >= 0; i--) if(l + (1 << i) - 1 <= r) {
int x = st[l][i]; l = l + (1 << i);
int y = x / val;
if(x - val * y) return 0;
}
return 1;
}

void init_block(int siz) {
int num = 1, cnt = 1;
while(num < n + 5) {
for(int i = 0; i < siz && num < n + 5; i++) block[num++] = cnt;
cnt++;
}
}

ll calculate_sum(ll i, ll l, ll r) {
ll sum = 0;
for(ll x = 0; x < gl[i].size(); x++) {
P info = gl[i][x];
ll ans = info.g;
ll L = max(l, (ll)info.l), R = min(r, (ll)info.r);
if(L > R) break; sum += ans * (R - L + 1);
}
for(ll x = 0; x < gr[i].size(); x++) {
P info = gr[i][x];
ll ans = info.g;
ll L = max(l, (ll)info.l), R = min(r, (ll)info.r);
if(L > R) break; sum += ans * (R - L + 1);
}
return sum - a[i];
}

void solve(ll &las_ans, ll lasl, ll lasr, ll nowl, ll nowr, int id) {
while(lasl < nowl) { las_ans -= calculate_sum(lasl, lasl, lasr); lasl++; }
while(lasl > nowl) { lasl--; las_ans += calculate_sum(lasl, lasl, lasr); }
while(lasr < nowr) { lasr++; las_ans += calculate_sum(lasr, lasl, lasr); }
while(lasr > nowr) { las_ans -= calculate_sum(lasr, lasl, lasr); lasr--; }
ans[id] = las_ans;
}

inline bool scan_d(int &num)
{
char in;bool IsN=false;
in=getchar();
if(in==EOF) return false;
while(in!='-'&&(in<'0'||in>'9')) in=getchar();
if(in=='-'){ IsN=true;num=0;}
else num=in-'0';
while(in=getchar(),in>='0'&&in<='9'){
num*=10,num+=in-'0';

4000
}
if(IsN) num=-num;
return true;
}

int main() {
scan_d(T);
while(T--) {
scan_d(n);
int bck = sqrt(n) + 1;
for(int i = 1; i <= n; i++) {
scan_d(a[i]); block[i] = i / bck;
st[i][0] = a[i];
gl[i].clear(); gr[i].clear();
}
init();
for(int i = 1; i <= n; i++) {
int lasg = a[i], from = i;
while(from) { ///log * log * log ???
int l = 1, r = from, g = gcd(lasg, a[from]);
while(l < r) {  ///log
int mid = (l + r) >> 1;
int flag = query(mid, from, g);
if(!flag) l = mid + 1;
else r = mid;
}
gl[i].push_back(P(l, from, g));
from = l - 1; lasg = g;
}
from = i; lasg = a[i];
while(from < n + 1) {
int l = from, r = n + 1, g = gcd(lasg, a[from]);
while(l < r) {
int mid = (l + r) >> 1;
int flag = query(from, mid, g);
if(flag) l = mid + 1;
else r = mid;
}
gr[i].push_back(P(from, r - 1, g));
from = r; lasg = g;
}
}
scan_d(Q);
for(int i = 1; i <= Q; i++) {
int l, r;
scan_d(l); scan_d(r);
q[i] = qry(block[l], l, r, i);
}
sort(q + 1, q + 1 + Q);
q[0] = qry(0, 1, 1, 0);
ll nowl, nowr, lasl = 1, lasr = 1, las_ans = a[1];
for(int i = 1; i <= Q; i++) {
nowl = (ll)q[i].l; nowr = (ll)q[i].r;
solve(las_ans, lasl, lasr, nowl, nowr, q[i].id);
lasl = nowl; lasr = nowr;
}
for(int i = 1; i <= Q; i++) printf("%lld\n", ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: