Loading

Nobody is needed

赛时没看 F,写完 E 就没时间了,唉唉,唉唉唉唉。

思路是 t 宝的代码。

Solution

考虑只有一组询问 l=1,r=n 的时候怎么做。

此时显然可以 dp,设 fi 为以 ai 结尾的合法子序列的个数。

得到方程

fi=1+ajaifj

1 是子序列中只有自己的情况。

答案即为 fi

对于原问题,考虑扫描线。

发现枚举 r 的话不是很好维护 dp 数组,因为对于每个 l 的 dp 数组是不一样的,所以这里枚举 l

假设此时枚举到 l,那所有 dpi 的值是 [l+1,i] 区间内以 i 结尾的合法子序列的数量。

fl 初始化为 1

如果直接按之前那样枚举的话,复杂度会炸到 n2。考虑 al 能影响到的数只有 al 的倍数,考虑每次只枚举这些数。

这些数每个会有一个增量,根据之前的 dp,发现增量也可以 dp 算出来,只需要将方程改成 fi=ajaifi 即可。

优化这个过程,可以通过先枚举 al 的倍数,假设是 k,此时 k 的增量已经被算出,再枚举 k 的倍数 kk 的增量加上 k 的增量,就能够保证 al 的所有倍数能够算出增量。

算出增量后,用树状数组维护 f 数组的区间和即可。

一轮枚举倍数是 O(nlnn) 的,要枚举所有倍数的倍数即为 O(nln2n),用树状数组让所有倍数加上增量是 O(nlnnlogn) 的,所以总复杂度为 O(nlog2n)

struct BIT {
	int n;
	vector<ll> c;

	BIT(int _n) {
		n = _n;
		c.resize(n + 1, 0);
	}

	void add(int x, int v) {
		for (; x <= n; x += x & -x) c[x] += v;
	}

	ll query(int x) {
		ll res = 0;
		for (; x; x -= x & -x) res += c[x];
		return res;
	}

	ll query(int l, int r) {
		l --;
		return query(r) - query(l);
	}
};

void solve() {
	int n, m;
	cin >> n >> m;
	vector<int> a(n + 1), p(n + 1);
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		p[a[i]] = i;
	}
	
	vector<vector<pair<int, int>>> qry(n + 1);
	for (int i = 1; i <= m; i ++) {
		int l, r;
		cin >> l >> r;
		qry[l].emplace_back(r, i);
	}

	vector<ll> ans(m + 1);
	vector<ll> g(n + 1);
	BIT bit(n);

	for (int l = n; l >= 1; l --) {
		int x = a[l];
		g[x] = 1;
		for (int i = x; i <= n; i += x) {
			if (p[i] < p[x]) continue;
			for (int j = i + i; j <= n; j += i) {
				if (p[j] < p[i]) continue;
				g[j] += g[i];
			}
		}
		for (int i = x; i <= n; i += x) {
			if (p[i] < p[x]) continue;
			bit.add(p[i], g[i]);
			g[i] = 0;
		}
		for (auto [r, id] : qry[l]) ans[id] = bit.query(r);
	}
	for (int i = 1; i <= m; i ++) cout << ans[i] << " \n"[i == m];
} 
posted @   Svemit  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示