题解: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;
}
posted @ 2026-02-02 17:11  Super_lollipop  阅读(2)  评论(0)    收藏  举报