大学連合アルゴリズムウィンター学院第5回(累計,ダブルポインタ)
44308 ワード
1. prefix sum
prefix sumは累積を表す
ぎじゅつ
prefix sum:推定値の保存
DPの問題に近いように見えます.
質問する
11659区間と4を求めます
区間求和問題は問題の制限が大きいため,時間制限内に通過できない.
O(NM)の時間が必要です.
prefix sumを使って
以上のようにpre表が記入されていれば4,6の和を求めることができる.
このようにあらかじめ計算して出力すると,N回の繰り返し文を迂回するだけなので,O(N)だけで問題を終わらせることができる.#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> a(n+1);
vector<ll> pre(n+1);
pre[0] = 0;
for (i = 1; i <= n; ++i) {
int temp;
cin >> temp;
a.push_back(temp);
pre[i] = pre[i - 1] + temp;
}
while (m--) {
int s, e;
cin >> s >> e;
int ans = 0;
cout << pre[e]-pre[s-1] << '\n';
}
}
しかし、このようにして中間的に値下がりすれば、何のメリットもありません.
したがって、「セグメントツリー」を使用すると、演算プロセスにO(logn)の時間がかかります.
11660区間加算5
この問題も区間と4を求めることに似ている.
欲しい部分の上と左を除いて、共通の部分を加えればいいです.
では、このような方式のためにpre[i][j]をどのように実施するのか.
正解は次のとおりです.
𝑎𝑛𝑠 = 𝑝𝑟𝑒[𝑐][𝑑] − 𝑝𝑟𝑒[𝑎 − 1][d] − 𝑝𝑟𝑒[c][𝑏 − 1] + 𝑝𝑟𝑒[𝑎 − 1][𝑏 − 1]
正解を求めるためには、次の式で求めることができます.
𝑝𝑟𝑒[𝑖][𝑗] = 𝑝𝑟𝑒[𝑖][𝑗 − 1] + 𝑝𝑟𝑒[𝑖 − 1][𝑗] − 𝑝𝑟𝑒[𝑖 − 1][𝑗 − 1] + 𝐴[𝑖][𝑗];#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int a[1025][1025];
int pre[1025][1025];
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (i = 1; i <= n; ++i) {
for (j = 1; j <= n; ++j) {
cin >> a[i][j];
pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + a[i][j];
}
}
while (m--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << pre[x2][y2] - pre[x1 - 1][y2] - pre[x2][y1 - 1] + pre[x1 - 1][y1 - 1] << '\n';
}
}
2. two pointer
デュアルポインタは、アレイ内の2つの異なる要素を指す点を操作し、問題を解決します.
質問する
2003ツリーの和2
この問題はprefix sumだけでは不可能です.O(n^3)の時間が必要だからです.
最も簡単な解法はtwo pointerを用い,所望の値より小さい場合はb,1より大きい場合は1を加える.#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
vector<ll> a;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (i = 0; i < n; ++i) {
ll temp;
cin >> temp;
a.push_back(temp);
}
int s = 0, e = 0;
ll sum = 0;
ll ans = 0;
while (1) {
if (sum >= m) sum -= a[s++];
else if (e == n) break;
else sum += a[e++];
if (sum == m) ans += 1;
}
cout << ans << '\n';
}
1484ダイエット
これはダイエットの問題です.アレイに平方数を加えて、二重ポインタで解きましょう.#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
vector<ll> a;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll d = 0;
cin >> d;
for (ll i = 1; i < 200001; ++i) {
a.push_back(i * i);
}
vector<ll> ans;
ll s = 0, e = 0;
ll diff = 0;
while (1) {
if (e >= a.size()) break;
diff = a[e] - a[s];
if (diff < d) e++;
if(diff>d) s++;
if (diff == d) {
ans.push_back(sqrt(a[e]));
e++;
}
}
if (!ans.size()) {
cout << "-1" << '\n';
}
else {
for (ll i : ans) {
cout << i << '\n';
}
}
return 0;
}
1253はい
この問題には2つの落とし穴がある.
1.数値に負/正があります.
[原句]自分以外なら何でもいい.
したがって,二重ポインタと並べ替えを用いて解くことができる.#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll n = 0;
cin >> n;
vector<ll> a(n);
for (i = 0; i < n; ++i) {
cin >> a[i];
}
sort(a.begin(), a.end());
ll ans = 0;
for (int i = 0; i< n; ++i) {
int s = 0, e = n - 1;
ll target = a[i];
while (s < e) {
ll sum = a[s] + a[e];
if (sum < target) {
s+=1;
}
else if (sum > target) {
e-=1;
}
else if(sum==target){
if (s != i and e != i) {
//cout << target << ' ' << a[s] << ' ' << a[e] << '\n';
ans += 1;
break;
}
else if (s == i) {
s += 1;
}
else if(e==i){
e -= 1;
}
}
}
}
cout << ans << '\n';
}
Reference
この問題について(大学連合アルゴリズムウィンター学院第5回(累計,ダブルポインタ)), 我々は、より多くの情報をここで見つけました
https://velog.io/@tonyhan18/대학-연합-알고리즘-윈터-스쿨-5회차
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<ll> a(n+1);
vector<ll> pre(n+1);
pre[0] = 0;
for (i = 1; i <= n; ++i) {
int temp;
cin >> temp;
a.push_back(temp);
pre[i] = pre[i - 1] + temp;
}
while (m--) {
int s, e;
cin >> s >> e;
int ans = 0;
cout << pre[e]-pre[s-1] << '\n';
}
}
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int a[1025][1025];
int pre[1025][1025];
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (i = 1; i <= n; ++i) {
for (j = 1; j <= n; ++j) {
cin >> a[i][j];
pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + a[i][j];
}
}
while (m--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << pre[x2][y2] - pre[x1 - 1][y2] - pre[x2][y1 - 1] + pre[x1 - 1][y1 - 1] << '\n';
}
}
デュアルポインタは、アレイ内の2つの異なる要素を指す点を操作し、問題を解決します.
質問する
2003ツリーの和2
この問題はprefix sumだけでは不可能です.O(n^3)の時間が必要だからです.
最も簡単な解法はtwo pointerを用い,所望の値より小さい場合はb,1より大きい場合は1を加える.
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
vector<ll> a;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (i = 0; i < n; ++i) {
ll temp;
cin >> temp;
a.push_back(temp);
}
int s = 0, e = 0;
ll sum = 0;
ll ans = 0;
while (1) {
if (sum >= m) sum -= a[s++];
else if (e == n) break;
else sum += a[e++];
if (sum == m) ans += 1;
}
cout << ans << '\n';
}
1484ダイエット
これはダイエットの問題です.アレイに平方数を加えて、二重ポインタで解きましょう.
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
vector<ll> a;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll d = 0;
cin >> d;
for (ll i = 1; i < 200001; ++i) {
a.push_back(i * i);
}
vector<ll> ans;
ll s = 0, e = 0;
ll diff = 0;
while (1) {
if (e >= a.size()) break;
diff = a[e] - a[s];
if (diff < d) e++;
if(diff>d) s++;
if (diff == d) {
ans.push_back(sqrt(a[e]));
e++;
}
}
if (!ans.size()) {
cout << "-1" << '\n';
}
else {
for (ll i : ans) {
cout << i << '\n';
}
}
return 0;
}
1253はい
この問題には2つの落とし穴がある.
1.数値に負/正があります.
[原句]自分以外なら何でもいい.
したがって,二重ポインタと並べ替えを用いて解くことができる.
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
#include <cstdlib>
#include <vector>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#define ll long long
#define len 1001
using namespace std;
int ans, i, j, n, m;
int main(void) {
freopen("input.txt", "r", stdin);
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll n = 0;
cin >> n;
vector<ll> a(n);
for (i = 0; i < n; ++i) {
cin >> a[i];
}
sort(a.begin(), a.end());
ll ans = 0;
for (int i = 0; i< n; ++i) {
int s = 0, e = n - 1;
ll target = a[i];
while (s < e) {
ll sum = a[s] + a[e];
if (sum < target) {
s+=1;
}
else if (sum > target) {
e-=1;
}
else if(sum==target){
if (s != i and e != i) {
//cout << target << ' ' << a[s] << ' ' << a[e] << '\n';
ans += 1;
break;
}
else if (s == i) {
s += 1;
}
else if(e==i){
e -= 1;
}
}
}
}
cout << ans << '\n';
}
Reference
この問題について(大学連合アルゴリズムウィンター学院第5回(累計,ダブルポインタ)), 我々は、より多くの情報をここで見つけました https://velog.io/@tonyhan18/대학-연합-알고리즘-윈터-스쿨-5회차テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol