题解:P9335 [Ynoi2001] 雪に咲く花
前言
从这题过来的。
思路
这俩题都有一个经典结论:与运算、或运算和取最大公约数,这些数一共不超过 \(\log V\) 种,证明如下:
- 对一个数和另一数与运算,要不然这个数不变,否则它至少有一位从 \(1\) 变成 \(0\)。
- 同样的,或运算会使这个数不变,或者至少有一位从 \(0\) 变成 \(1\)。
- 同理,取最大公约数可能这个数不变,或者至少会使最大公约数除以二,因为 \(2\) 是最小的质因数。
得到这个性质之后,我们可以直接离线扫描线,考虑每次新加一个数,暴力与前面的合并,这样复杂度是对的,由上面的性质可知,这样只需要合并 \(n\log V\) 次。代码很好写,这题关键在卡常。
卡常
这里提供一些卡常方法仅供参考:
- 换用更快的 \(\gcd\),建议使用手写二进制优化更相减损术 \(\gcd\)。
- 换更快的快读,正常的快读已经适应不了这个题了,建议找一个封装的快读使用。我加上快读直接就过了。
- 不要使用
vector,可以考虑使用计数排序或者手写前向星。 - 如果还是不过,多换几个语言多交几遍。
代码(未卡常)
int query(int p){ return val[p] + add[p] * (T - nt[p]);}
signed main(){
#ifndef ONLINE_JUDGE
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
#endif
ios::sync_with_stdio(false),cin.tie(nullptr);
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++) cin >> b[i];
for(int i = 1; i <= n; i++) cin >> c[i];
for(int i = 1, l, r; i <= m; i++) cin >> l >> r, vec[r].pb({l, i});
for(int i = 1; i <= n; i++){
int p = i - 1;
while(p != 0){
int u = a[p] & a[p + 1];
int v = b[p] | b[p + 1];
int w = gcd(c[p], c[p + 1]);
if(u == a[p] && v == b[p] && w == c[p]) break;
a[p] = u, b[p] = v, c[p] = w; --p;
}
val[i] = query(i - 1);
for(int j = p + 1; j <= i; j++){
val[j] = query(j);
add[j] = add[j - 1] + a[j] * b[j] * c[j];
nt[j] = T;
}
++T;
for(auto [l, id] : vec[i]) ans[id] = query(i) - query(l - 1);
}
for(int i = 1; i <= m; i++) cout << ans[i] << '\n';
return 0;
}

浙公网安备 33010602011771号