常见算法

莫队算法

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 1e9;
const ll INF = 1e18;
const int N=5e4+10;
int bl;
ll sum;
struct node
{
int l,r,id;
}v[N];
int a[N],cnt[N];
bool cmp(node &a,node&b)
{
if(a.l/bl!=b.l/bl)return a.l<b.l;
return a.r<b.r;
}
void add(int x)
{
sum+=2*(cnt[x]++)+1;
}
void del(int x)
{
sum+=-2*(cnt[x]--)+1;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,k;
cin>>n>>m>>k;
bl=sqrt(n);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=m;i++)
{
cin>>v[i].l>>v[i].r;
v[i].id=i;
}
vector<int>ans(m+1);
sort(v+1,v+1+m,cmp);
for(int i=1,l=1,r=0;i<=m;i++)
{
while(l>v[i].l)add(a[--l]);
while(r<v[i].r)add(a[++r]);
while(l<v[i].l)del(a[l++]);
while(r>v[i].r)del(a[r--]);
ans[v[i].id]=sum;
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';

return 0;
}

线段树(静态区间查询)

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 1e9;
const ll INF = 1e18;
template<class Info>
struct SegmentTree {
int n;
std::vector<Info> info;
SegmentTree() : n(0) {}
SegmentTree(int n_, Info v_ = Info()) {
init(n_, v_);
}
template<class T>
SegmentTree(std::vector<T> init_) {
init(init_);
}
void init(int n_, Info v_ = Info()) {
init(std::vector(n_, v_));
}
template<class T>
void init(std::vector<T> init_) {
n = init_.size();
info.assign(4 << std::__lg(n), Info());
std::function<void(int, int, int)> build = [&](int p, int l, int r) {
if (r - l == 1) {
info[p] = init_[l];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m, r);
pull(p);
};
build(1, 0, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void modify(int p, int l, int r, int x, const Info &v) {
if (r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
if (x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int p, const Info &v) {
modify(1, 0, n, p, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l >= y || r <= x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 0, n, l, r);
}
template<class F>
int findFirst(int p, int l, int r, int x, int y, F pred) {
if (l >= y || r <= x || !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
int res = findFirst(2 * p, l, m, x, y, pred);
if (res == -1) {
res = findFirst(2 * p + 1, m, r, x, y, pred);
}
return res;
}
template<class F>
int findFirst(int l, int r, F pred) {
return findFirst(1, 0, n, l, r, pred);
}
template<class F>
int findLast(int p, int l, int r, int x, int y, F pred) {
if (l >= y || r <= x || !pred(info[p])) {
return -1;
}
if (r - l == 1) {
return l;
}
int m = (l + r) / 2;
int res = findLast(2 * p + 1, m, r, x, y, pred);
if (res == -1) {
res = findLast(2 * p, l, m, x, y, pred);
}
return res;
}
template<class F>
int findLast(int l, int r, F pred) {
return findLast(1, 0, n, l, r, pred);
}
};
struct Info {
int g;
};
Info operator+(Info a, Info b) {
return {gcd(a.g,b.g)};
}

void solve(){
int n,q;
cin>>n>>q;
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
vector<Info>init(n);
for(int i=0;i<n;i++)init[i].g=abs(a[i+1]-a[i]);
SegmentTree<Info>seg(init);
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
cout<<seg.rangeQuery(l,r).g<<" \n"[i==q];
}

}

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;cin>>tt;
while(tt--){
solve();
}


return 0;
}

单调栈

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 1e9;
const ll INF = 1e18;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while(cin>>n,n)
{
vector<int>l(n+1,0),r(n+1,n+1);
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
stack<int>stk;
for(int i=1;i<=n;i++)
{
while(stk.size()&&a[i]<a[stk.top()])
{
r[stk.top()]=i;
stk.pop();
}
stk.push(i);
}
stack<int>stk1;
for(int i=n;i>=1;i--)
{
while(stk1.size()&&a[i]<a[stk1.top()])
{
l[stk1.top()]=i;
stk1.pop();
}
stk1.push(i);
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ll shu=1ll*(r[i]-l[i]-1)*a[i];
ans=max(ans,shu);
}
cout<<ans<<'\n';
}

return 0;
}

悬线法

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
#define int ll
using u64 = unsigned long long;
const ll inf = 1e9;
const ll INF = 1e18;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
while(cin>>n,n)
{
vector<int>a(n+1),l(n+1),r(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)l[i]=r[i]=i;
for(int i=1;i<=n;i++)while(l[i]>1&&a[i]<=a[l[i]-1])l[i]=l[l[i]-1];
for(int i=n;i>=1;i--)while(r[i]<n&&a[i]<=a[r[i]+1])r[i]=r[r[i]+1];
int maxn=-inf;
for(int i=1;i<=n;i++)
{
int shu=(long long)(r[i]-l[i]+1)*a[i];
maxn=max(maxn,shu);
}
cout<<maxn<<'\n';
}
return 0;
}

对顶堆

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const int inf = 2147483647;
const ll INF = 1e18;
void solve(){
int x;
priority_queue<int,vector<int>,greater<int>>a;
priority_queue<int,vector<int>,less<int>>b;
while(cin>>x&&x)
{
if(x==-1)
{
cout<<b.top()<<'\n';
b.pop();
}
else
{
if(b.size()&&x<=b.top())b.push(x);//感觉就是随便选一个塞进去,然后再调整
else a.push(x);
}
if(b.size()>(a.size()+b.size()+1)/2)
{
a.push(b.top());
b.pop();
}
else if(b.size()<(a.size()+b.size()+1)/2)
{
b.push(a.top());
a.pop();
}
}
}

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt;cin>>tt;
while(tt--){
solve();
}


return 0;
}

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const int inf = 2147483647;
const ll INF = 1e18;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
vector<int>k(n+1),v(m+1);
priority_queue<int,vector<int>,greater<int>>a;
priority_queue<int,vector<int>,less<int>>b;
for(int i=1;i<=n;i++)cin>>k[i];
for(int i=1;i<=m;i++)cin>>v[i];
sort(v.begin()+1,v.end());
int dix=1;
int geshu=0;
for(int i=1;i<=n;i++)
{
int x=k[i];
// 感觉这一种思路是最方便的
b.push(x);// 这三行代码直接把x添加到b之中,让b先进行排序,然后再把堆顶元素弹到a中
a.push(b.top());
b.pop();
while(dix<=m&&v[dix]==i)
{
b.push(a.top());
a.pop();
cout<<b.top()<<"\n";
dix++;
}
}

return 0;
}

树状数组(逆序对)

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 1e9;
const ll INF = 1e18;
template<typename T>
struct Fenwick{
int n;
vector<T> tr;

Fenwick(int n) : n(n), tr(n + 1, 0){}

int lowbit(int x){// 找到x最低有效位
return x & -x;
}

void modify(int x, T c){// 点修
for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

void modify(int l, int r, T c){// 区修
modify(l, c);
if (r + 1 <= n) modify(r + 1, -c);
}

T query(int x){// 前缀和
T res = T();
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}

T query(int l, int r){// 区间查询
return query(r) - query(l - 1);
}

int find_first(T sum){// 查找第一个前缀和大于或等于 sum 的下标
int ans = 0; T val = 0;
for(int i = __lg(n); i >= 0; i--){
if ((ans | (1 << i)) <= n && val + tr[ans | (1 << i)] < sum){
ans |= 1 << i;
val += tr[ans];
}
}
return ans + 1;
}

int find_last(T sum){// 查找最后一个前缀和小于或等于 sum 的下标
int ans = 0; T val = 0;
for(int i = __lg(n); i >= 0; i--){
if ((ans | (1 << i)) <= n && val + tr[ans | (1 << i)] <= sum){
ans |= 1 << i;
val += tr[ans];
}
}
return ans;
}

};
using BIT = Fenwick<int>;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
vector<int>p(n+1);
iota(p.begin(),p.end(),0);
sort(p.begin()+1,p.end(),[&](int i,int j){
if(a[i]!=a[j])return a[i]>a[j];//第一个p[i]为b[i]中最大元素的下标
return i>j;如果两个一样大,那么这个下标大的靠前,这样后面使用bit.query时相同大小的不会相互影响。
});
BIT bit(n);
ll ans=0;
for(int i=1;i<=n;i++)
{
ans+=bit.query(p[i]);
bit.modify(p[i],1);
}
cout<<ans<<'\n';
return 0;
}

背包DP

多重背包

P1776 宝物筛选

https://www.luogu.com.cn/problem/P1776

这一题很傻鸟,他先给的物品的价值,再给的物品的重量,所以要cin>>w>>v>>s;

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
// 多重背包的朴素算法,但是会TLE
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 2147483647;
const ll INF = 1e18;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
vector<ll>dp(m+1);
for(int i=1;i<=n;i++)
{
int w,v,g;
cin>>w>>v>>g;
for(int j=m;j>=v;j--)
{
for(int k=1;k<=g;k++)
{
if(j>=k*v)dp[j]=max(dp[j],dp[j-k*v]+k*w);
else break;
}
}
}
cout<<dp[m]<<'\n';
return 0;
}
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
// 多重背包的二进制优化
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 2147483647;
const ll INF = 1e18;
struct node
{
int v,w;
};
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
vector<ll>dp(m+1);
vector<node>goods;
for(int i=1;i<=n;i++)
{
int w,v,s;
cin>>w>>v>>s;
for(int j=1;j<=s;j<<=1)
{
s-=j;
goods.push_back({j*v,j*w});
}
if(s)goods.push_back({s*v,s*w});
}
for(auto node:goods)
{
for(int j=m;j>=node.v;j--)
{
dp[j]=max(dp[j],dp[j-node.v]+node.w);
}
}
cout<<dp[m]<<'\n';
return 0;
}

混合背包

混合背包就是把01背包,完全背包,多重背包合在一起了,如果对前面理解透彻的话这个应该也没有什么问题。

如果是用二维DP数组递推的话,01背包是从左上方递推而来,完全背包是从同一层的左边递推而来,而多重背包则是一种特殊的01背包,由多个符合条件的左上方递推而来,多了一个循环遍历个数而已

P1833 樱花

https://www.luogu.com.cn/problem/P1833

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 2147483647;
const ll INF = 1e18;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string a,b;
cin>>a>>b;
int n;
cin>>n;
int zuo=0,you=0;
if(a.size()==4)zuo=(a[0]-'0')*60+stoi(a.substr(2));
else zuo=(stoi(a.substr(0,2))*60+stoi(a.substr(3)));
if(b.size()==4)you=(b[0]-'0')*60+stoi(b.substr(2));
else you=(stoi(b.substr(0,2))*60+stoi(b.substr(3)));
int m=you-zuo;
vector<ll>dp(m+1);
for(int i=1;i<=n;i++)
{
int v,w,s;
cin>>v>>w>>s;
if(s==0)
{
for(int j=v;j<=m;j++)dp[j]=max(dp[j],dp[j-v]+w);
}
else if(s==1)
{
for(int j=m;j>=v;j--)dp[j]=max(dp[j],dp[j-v]+w);
}
else
{
for(int j=m;j>=v;j--)
{
for(int k=1;k<=s;k++)
{
if(j>=k*v)dp[j]=max(dp[j],dp[j-k*v]+k*w);
}
}
}
}
cout<<dp[m]<<'\n';
return 0;
}

二维费用背包

二维费用背包基本上和一维的01背包是一样的,只不过多开了一个维度而已

P1855 榨取kkksc03

https://www.luogu.com.cn/problem/P1855

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 2147483647;
const ll INF = 1e18;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,t;
cin>>n>>m>>t;
vector<vector<int>>dp(m+1,vector<int>(t+1));
for(int i=1;i<=n;i++)
{
int a,b;
cin>>a>>b;
for(int j=m;j>=a;j--)
{
for(int k=t;k>=b;k--)
{
dp[j][k]=max(dp[j][k],dp[j-a][k-b]+1);
}
}
}
cout<<dp[m][t]<<'\n';
return 0;
}

分组背包

分组背包也是从左上角递推过来,只不过这个是由一组中的多个物品分别递推而来,而多重背包是由第i个物品的第k倍递推而来

P1757 通天之分组背包

https://www.luogu.com.cn/problem/P1757

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
//#define int ll
using u64 = unsigned long long;
const ll inf = 2147483647;
const ll INF = 1e18;
struct node
{
int v,w;
};
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m;
cin>>m>>n;
vector<ll>dp(m+1);
int k=-inf;
vector<vector<node>>a(n+1);
for(int i=1;i<=n;i++)
{
int v,w,s;
cin>>v>>w>>s;
k=max(k,s);
a[s].push_back({v,w});
}

for(int i=1;i<=k;i++)
{
for(int j=m;j>=1;j--)
{
for(auto [v,w]:a[i])
{
if(j>=v)dp[j]=max(dp[j],dp[j-v]+w);
}
}
}
cout<<dp[m]<<'\n';
return 0;
}

有依赖的背包

P1064 [NOIP2006 提高组] 金明的预算方案

https://www.luogu.com.cn/problem/P1064

这一题有一说一感觉也是一种分组背包,只不过需要细分进行讨论而已。因为一个物品的附属物品最多只有两件,所以可以直接枚举进行讨论。

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
#include<bits/stdc++.h>
using namespace std;
using u32 = unsigned;
#define i128 __int128;
using ll = long long;
using u64 = unsigned long long;
const int inf = 2147483647;
const ll INF = 1e18;
struct node
{
int v,p,q;
int jz;
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m,n;
cin>>m>>n;
vector<node>v(n+1);
vector<vector<int>>adj(n+1);
vector<ll>dp(m+1);
for(int i=1;i<=n;i++)
{
cin>>v[i].v>>v[i].p>>v[i].q;
v[i].jz=v[i].v*v[i].p;
if(v[i].q)adj[v[i].q].push_back(i);
}
for(int i=1;i<=n;i++)
{
if(v[i].q)continue;
for(int j=m;j>=v[i].v;j--)
{
dp[j]=max(dp[j],dp[j-v[i].v]+v[i].jz);
if(adj[i].size()==2)
{
if(j>=v[i].v+v[adj[i][0]].v)dp[j]=max(dp[j],dp[j-v[i].v-v[adj[i][0]].v]+v[i].jz+v[adj[i][0]].jz);

if(j>=v[i].v+v[adj[i][0]].v+v[adj[i][1]].v)dp[j]=max(dp[j],dp[j-v[i].v-v[adj[i][0]].v-v[adj[i][1]].v]+v[i].jz+v[adj[i][0]].jz+v[adj[i][1]].jz);

if(j>=v[i].v+v[adj[i][1]].v)dp[j]=max(dp[j],dp[j-v[i].v-v[adj[i][1]].v]+v[i].jz+v[adj[i][1]].jz);

}
else if(adj[i].size()==1)
{
if(j>=v[i].v+v[adj[i][0]].v)dp[j]=max(dp[j],dp[j-v[i].v-v[adj[i][0]].v]+v[i].jz+v[adj[i][0]].jz);
}
}

}
cout<<dp[m]<<'\n';
return 0;
}