洛谷 P10463 Interval GCD Solution

Description

给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,,an),有 mmm 个操作分两种:

  • add⁡(l,r,k)\operatorname{add}(l,r,k)add(l,r,k):对每个 i∈[l,r]i\in[l,r]i[l,r] 执行 ai←ai+ka_i\gets a_i+kaiai+k.
  • query⁡(l,r)\operatorname{query}(l,r)query(l,r):求 ∣gcd⁡(al,al+1,⋯ ,ar)∣|\gcd(a_l,a_{l+1},\cdots,a_r)|gcd(al,al+1,,ar).

Limitations

1≤n≤5×1051 \le n \le 5\times 10^51n5×105
1≤m≤1051 \le m \le 10^51m105
1≤ai≤10181 \le a_i \le 10^{18}1ai1018
∣k∣≤1018|k|\le 10^{18}k1018
任意时刻所有数均在 2632^{63}263 以内.
1s,512MB1\text{s},512\text{MB}1s,512MB

Solution

区间加区间 gcd⁡\gcdgcd 不太好做,考虑转化成单点加.
首先要知道一条性质:gcd⁡(x,y)=gcd⁡(x,y−x)\gcd(x,y)=\gcd(x,y-x)gcd(x,y)=gcd(x,yx).
那么就可以推式子了:
  gcd(al,al+1,⋯ ,ar)=gcd(al,al+1−al,al+1,al+2−al+1,⋯ ,ar,ar−ar−1)=gcd(al,al+1−al,al+2−al+1,⋯ ,ar−ar−1) \begin{aligned} &\quad\; gcd(a_l,a_{l+1},\cdots,a_r)\\ &=gcd(a_l,a_{l+1}-a_l,a_{l+1},a_{l+2}-a_{l+1},\cdots,a_r,a_r-a_{r-1})\\ &=gcd(a_l,a_{l+1}-a_l,a_{l+2}-a_{l+1},\cdots,a_r-a_{r-1}) \end{aligned} gcd(al,al+1,,ar)=gcd(al,al+1al,al+1,al+2al+1,,ar,arar1)=gcd(al,al+1al,al+2al+1,,arar1)
发现这就是差分,所以把差分数组拿到线段树上维护,就只需要单点加了.
用树状数组维护一下 ala_lal 即可,注意答案要 abs.

Code

2.96KB,686ms,42.48MB  (in total, C++20 with O2)2.96\text{KB},686\text{ms},42.48\text{MB}\;\texttt{(in total, C++20 with O2)}2.96KB,686ms,42.48MB(in total, C++20 with O2)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

int lowbit(int x){
	return x & -x;
}

template<class T>
struct fenwick{
	int n;
	vector<T> c;
	
	fenwick() {}
	fenwick(int _n): n(_n){
		c.resize(n + 1);
	}
	
	fenwick(const vector<T> &a): n(a.size()){
		c.resize(n + 1);
		for(int i = 1; i <= n; i++){
			c[i] = c[i] + a[i - 1];
			int j = i + lowbit(i);
			if(j <= n) c[j] = c[j] + c[i];
		}
	}
	
	void add(int x, const T& v){
		for(int i = x + 1; i <= n; i += lowbit(i)) c[i] = c[i] + v;
	}
	
	T ask(int x){
		T ans{};
		for(int i = x + 1; i; i -= lowbit(i)) ans = ans + c[i];
		return ans;
	}
	
	T ask(int l, int r){
		return ask(r) - ask(l - 1);
	}
};

namespace seg_tree {
	inline int ls(int u) { return 2 * u + 1; }
	inline int rs(int u) { return 2 * u + 2; }
	
	struct Node {
		int l, r;
		i64 val;
	};
	
	struct SegTree {
		vector<Node> tr;
		inline SegTree() {}
		inline SegTree(const vector<i64>& a) {
			const int n = a.size();
			tr.resize(n << 2);
			build(0, 0, n - 1, a);
		}
		
		inline void pushup(int u) {
			tr[u].val = ::gcd(tr[ls(u)].val, tr[rs(u)].val);
		}
		
		inline void build(int u, int l, int r, const vector<i64>& a) {
			tr[u].l = l, tr[u].r = r;
			if (l == r) {
				tr[u].val = a[l];
				return;
			}
			const int mid = (l + r) >> 1;
			build(ls(u), l, mid, a);
			build(rs(u), mid + 1, r, a);
			pushup(u);
		}
		
		inline void add(int u, int p, i64 x) {
			if (tr[u].l == tr[u].r) {
				tr[u].val += x;
				return;
			}
			const int mid = (tr[u].l + tr[u].r) >> 1;
			if (p <= mid) add(ls(u), p, x);
			else add(rs(u), p, x);
			pushup(u);
		}
		
		inline i64 gcd(int u, int l, int r) {
			if (l <= tr[u].l && tr[u].r <= r) return llabs(tr[u].val);
			const int mid = (tr[u].l + tr[u].r) >> 1;
			i64 res = 0;
			if (l <= mid) res = ::gcd(res, gcd(ls(u), l, r));
			if (r > mid) res = ::gcd(res, gcd(rs(u), l, r));
			return llabs(res);
		}
	};
}
using seg_tree::SegTree;

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m;
	cin >> n >> m;
	
	vector<i64> a(n);
	for (int i = 0; i < n; i++) cin >> a[i];
	
	auto [fwk, sgt] = [&]() {
		vector<i64> b(n);
		adjacent_difference(a.begin(), a.end(), b.begin());
		return make_pair(fenwick<i64>(b), SegTree(b));
	}();
	
	auto query = [&](int l, int r) {
		return gcd(fwk.ask(l), (l < r ? sgt.gcd(0, l + 1, r) : 0LL));
	};
	auto add = [&](int l, int r, i64 v) {
		fwk.add(l, v), sgt.add(0, l, v);
		if (r + 1 < n) fwk.add(r + 1, -v), sgt.add(0, r + 1, -v);
	};
	
	for (int i = 0; i < m; i++) {
		char op; int l, r; i64 v;
		cin >> op >> l >> r, l--, r--;
		if (op == 'C') add(l, r, (cin >> v, v));
		else cout << query(l, r) << endl;
	}
	
	return 0;
}
### 关于 P1609 的题目解析 目前并未提供具体的引用内容来描述 P1609 的相关信息。然而,基于常见的编程竞赛题型以及类似的动态规划或贪心算法问题,可以推测该题目可能涉及某种优化策略。 #### 假设的解法思路 如果假设 P1609 是一道关于资源分配或者路径寻找的问题,则可以通过以下方法解决: 1. **动态规划** 动态规划是一种常用的解决问题的方法,在许多情况下能够有效地找到最优解。例如,对于某些最大化收益或最小化成本的问题,定义状态转移方程是非常重要的[^3]。 ```cpp // 示例代码框架(假设为背包问题) #include <iostream> #include <vector> using namespace std; const int MAX_N = 1e3 + 5; int dp[MAX_N]; int main(){ int N, W; cin >> N >> W; vector<int> weight(N), value(N); for(int i=0;i<N;i++) cin>>weight[i]; for(int i=0;i<N;i++) cin>>value[i]; for(int i=0;i<N;i++){ for(int j=W;j>=weight[i];j--){ dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); } } cout<<dp[W]<<endl; return 0; } ``` 2. **贪心算法** 如果问题是关于局部最优解的选择,那么贪心算法可能是更高效的方式。通过每次选择当前最佳选项逐步构建全局最优解[^4]。 ```cpp // 贪心算法示例代码(假设为区间覆盖问题) #include <bits/stdc++.h> using namespace std; struct Interval{ int start,end; }; bool cmp(const Interval& a,const Interval& b){ return a.end<b.end; } int main(){ int n; cin>>n; vector<Interval> intervals(n); for(auto &i:intervals) cin>>i.start>>i.end; sort(intervals.begin(),intervals.end(),cmp); int count=0,last=-1; for(auto &i:intervals){ if(last<i.start){ last=i.end; count++; } } cout<<count<<endl; return 0; } ``` #### 总结 虽然具体到 P1609 的细节尚未给出,但从上述分析可以看出,无论是动态规划还是贪心算法都可以作为通用解决方案的一部分。实际应用时需根据题目条件调整参数设置和逻辑流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值