把过去和现在的笔记记录也搬了过来,也算是给以后留个念想吧,想想一开始打acm就是图一乐,后来发现这游戏还挺上头的,也算是度过了一段电竞生涯(xs)
早些时候的笔记写的好中二,连我自己看着都羞耻。
不过,就喜欢这种羞耻的感觉。
收录的题目大部分是个人认为质量不错的题目,以DP为主,非DP的题目都用※进行了标识。
当然,有些题解思路本身也是源自其他人的,不过除非特殊标注,否则都是用的自己的代码。

题目大意:

CF1548B Intergers Have Friend
题目大意:一个数组(2e5),要求其最大的一个子段,使得存在一个整数m>=2,该段内所有数模m后得到的结果相同。

解:

通过观察(猜测)很容易发现,该子段的差分数组gcd一定大于1,实际上这个gcd就是整数m,于是问题就转化成了求得一个最大子段,使得该段gcd>=2。

关于这题本人有一个惨烈的历程,首先想到了正解,后来t了,以为是这个解法的问题,之后去写了线段树,然后线段树是真正的t了,但我以为是我树的问题,于是坐牢一个小时,看题解发现说是可以用线段树和st表,仔细想想st表确实可以(但我不会?),我坚持线段树是不行的,于是去看人家的代码,点开榜一代码一看,草这不是我最开始的做法吗?然后发现自己vector没开long long,给负数求gcd导致t了……然后找了好久也没找到线段树做法,百思不得其解,后来发现官方一共有54个点,我的线段树t了第57个点,欧亨利式结尾了属于是。

那么这个正确的解法是什么呢?如果暴力地去求区间gcd肯定是不行的,但是我们可以递推的去求,因为一旦对于一个数来讲,如果它和前面某段区间的gcd为1,那么这段即使延伸到后面,它的gcd也一定为1了,因此我们可以果断地抛弃这段,而如果它和前面这段的gcd不为1,假设其为g,显然如果原来之前那段的gcd为f,那么g=f/k,这里被舍弃的这个k由于无法通过当前这个数传递到后面,也可以作废,换句话说,对于每个数,传递到下一个数的可能的gcd数量一定<=前面传来的gcd数量(因为可能会出现重复和1)+1,+1是因为自己本身一定是作为一个数传递到下面的gcd的,但是这个数本身一定是g的倍数,所以对于下一个数a[i]来讲,如果它和g的gcd大于1,那么这个新的gcd一定等于它和上一个a[i-1]的gcd,而如果不是这样的话,它虽然有可能是和a[i]的gcd>1,但还是绝对不可能和g跟a[i]产生两个不同的gcd,所以每个数传递到后面的gcd数量一定<=2,我可以直接用vector记录下这些gcd,滚不滚动都无所谓,这样解法就是O(nlog1e18)了。
而线段树的解法是O(2nlognlognlog1e18),st表是O(nlognlog1e18),全然不如这个dp做法。

Gcd的世界非常的奇妙,但既然已经证明过了,我选择将其记下来以后用就完了。

代码

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
#include <istream>
#include <map>
#include <cmath>
#include <stack>
#include <set>
#include <cstring>
#include <string>
#include <fstream>
#include <unordered_map>
#define ll long long
#define maxn 200005
#define mdl 1000000007
#define clr(a,n) for(int i=0;i<n;i++)a[i]=0
#define cfast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pll pair<ll,ll>
#define pii pair<int,int>
#define inc(i,a,n) for(int i=a;i<n;i++)
#define vset(a,n,m) for(int i=0;i<n;i++)a[i]=m;
#define endl '\n'
#define pi 3.141592657
using namespace std;
ll gcd(ll a, ll b) {
//if (a < b)swap(a, b);
//cout << "gcding " << a << " and " << b << endl;
if (b == 0)return a;
return gcd(b, a % b);
}
ll a[maxn], cf[maxn];
vector<ll> nums[maxn];
map<pll, ll> dp;
unordered_map<ll, ll> vis;
int main() {
cfast;
int t;
cin >> t;
while (t--) {
dp.clear();
int n;
cin >> n;
inc(i, 0, n) {
nums[i].clear();
cin >> a[i];
if (i > 0) {
cf[i] = abs(a[i] - a[i - 1]);
//cout << "cf." << i << " = " << cf[i] << endl;
}
}
if (n == 1) {
cout << 1 << endl;
continue;
}
ll ans = 0;
inc(i, 1, n) {
vis.clear();
int len = nums[i - 1].size();
//cout << "len = " << len << endl;
inc(j, 0, len) {
ll g = gcd(nums[i - 1][j], cf[i]);
//cout << "g=" << g << endl;
if (g > 1 && vis[g] == 0) {
vis[g] = 1;
nums[i].push_back(g);
// cout << g << " pushed" << endl;
pll p, pf;
p.first = i, p.second = g;
pf.first = i - 1, pf.second = nums[i - 1][j];
dp[p] = max(dp[p], dp[pf] + 1);
ans = max(ans, dp[p]);
}
}
if (vis[cf[i]] == 0) {
nums[i].push_back(cf[i]);
// cout << cf[i] << " pushed" << endl;
}
pll p;
p.first = i, p.second = cf[i];
if (cf[i] == 1)dp[p] = max(dp[p], 0ll);
else dp[p] = max(dp[p], 1ll);
ans = max(ans, dp[p]);
}
cout << ans + 1 << endl;
}
}
/*
2022.5.2 8:14
*/