从裸题开始:
连续自然数(Tzoj 8514):
1. 为什么可以用双指针:连续整数段;
2. while (lef <= n / 2) 基于升序的一定程度的优化;
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<long, long>
typedef long long ll;
const int N = 1e5 + 7;
#define lowbit(x) (x & (-x))
ll rs(ll p) {return p << 1 | 1;}
ll ls(ll p) {return p << 1;}
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
int main() {
int n; cin >> n;
int lef = 1, rig = 2;
int sum = lef + rig;
while (lef <= n / 2) {
if (sum < n) {
rig++;
sum += rig;
}
else if (sum == n) {
cout << lef << " " << rig << endl;
rig++;
sum += rig;
}
else if (sum > n) {
sum -= lef;
lef++;
}
}
return 0;
}
逛画展(Tzoj 8515):
1. 同样是连续区间;
2. 每次纳入一幅画至维护区间中, 若 !f[rig] (f[i] 表示维护区间中 i th 名师数量), 则cnt++(当前选择区间中名师数量);
3. 当cnt == m(总名师数量) 时, 进行最短区间判断;
4. 最短区间的判断: 判断的最终目标是区间左端点的名师在维护区间中只有一幅作品(事实上判断是在不改变cnt的大小的前提下使得左端点最大);
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define lowbit(x) (x & -(x))
#define INF 0x3f3f3f3f
const int N = 1e6 + 7;
int f[N];
int a[N];
int main()
{
IOS;
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int cnt = 0;
int l, r;
int lef = 1, rig = 0;
int minn = INT_MAX;
while (rig <= n) {
rig++;
if(!f[a[rig]]) {
cnt++;
}
f[a[rig]] += 1;
if (cnt == m) {
while (f[a[lef]] > 1) {
f[a[lef]] -= 1;
lef++;
}
if (rig - lef + 1 < minn) {
minn = rig - lef + 1;
l = lef;
r = rig;
}
}
}
cout << l << " " << r ;
return 0;
}
单词背诵(Tzoj 8519):
1. 这题与上一题的不同在于最大值不确定 , 于是我扫了两遍;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define lowbit(x) (x & -(x))
#define INF 0x3f3f3f3f
const int N = 1e6 + 7;
vector<string> v(N);
map<string, bool> mp;
map<string, int> f;
int main()
{
IOS;
int n, m; cin >> n;
for (int i = 1; i <= n; i++) {
string t; cin >> t;
mp[t] = true;
}
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> v[i];
}
int cnt = 0;
int lef = 1, rig = 0;
int maxn = 0;
int minn = INT_MAX;
while (rig < m) {
rig++;
bool flag = false;
if (mp[v[rig]] && !f[v[rig]]) {
cnt++;
maxn = max(maxn, cnt);
}
if (mp[v[rig]]) f[v[rig]] += 1;
}
lef = 1, rig = 0;
f.clear();
cnt = 0;
while (rig < m) {
rig++;
if (mp[v[rig]] && !f[v[rig]]) {
cnt++;
}
if (mp[v[rig]]) f[v[rig]] += 1;
if (cnt == maxn) {
while(true) {
if (!mp[v[lef]]) lef++;
else if (f[v[lef]] > 1) {
f[v[lef]] -= 1;
lef++;
}
else break;
}
minn = min(minn, rig - lef + 1);
}
}
cout << maxn << endl;
cout << minn << endl;
return 0;
}
但事实上还有在线算法(不过我没写成在线的形式, 因为比较懒, 只是复杂度类似)
这里借两次minn(最短区间)的变化了解代码;
1. 第一种是cnt(最大涵盖字符数)增大, 优先级更高, 因为我们要寻找的是最大涵盖数的最小区间, 故 minn = rig - lef + 1, 直接覆盖原值;
2. 第二种是 cnt不变, 缩短区间(即寻找lef的最大值), 寻找原理与上题相同;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define lowbit(x) (x & -(x))
#define INF 0x3f3f3f3f
const int N = 1e6 + 7;
vector<string> v(N);
map<string, bool> mp;
map<string, int> f;
int main()
{
IOS;
int n, m; cin >> n;
for (int i = 1; i <= n; i++) {
string t; cin >> t;
mp[t] = true;
}
cin >> m;
for (int i = 1; i <= m; i++) {
cin >> v[i];
}
int cnt = 0;
int lef = 1, rig = 0;
int maxn = 0;
int minn = INT_MAX;
while (rig < m) {
rig++;
bool flag = false;
if (mp[v[rig]] && !f[v[rig]]) {
cnt++;
maxn = max(maxn, cnt);
flag = true;
}
if (mp[v[rig]]) f[v[rig]] += 1;
if (flag) {
minn = rig - lef + 1;
}
while(lef <= rig) {
if (!mp[v[lef]]) lef++;
else if (f[v[lef]] > 1) {
f[v[lef]] -= 1;
lef++;
}
else break;
}
minn = min(minn, rig - lef + 1);
}
cout << maxn << endl;
cout << minn << endl;
return 0;
}
看不出来:
差为给定数(Tzoj 7923):
我是用map写的, 不知道双指针怎么样, 还是这是另一种双指针?
一看就懂不解析, 感觉来了就是这样。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<long, long>
typedef long long ll;
const int N = 1e5 + 7;
#define lowbit(x) (x & (-x))
ll rs(ll p) {return p << 1 | 1;}
ll ls(ll p) {return p << 1;}
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
map<ll, ll> mp;
ll a[N];
int main() {
IOS;
ll n, m; cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
mp[a[i]]++;
}
ll ans = 0;
for (int i = 0; i < n - 1; i++) {
ans += mp[a[i] + m];
}
cout << ans;
return 0;
}
有点藏的题目:
Xor Sum 2(Tzoj 8520):
题意是给定n个数, 在连续区间内找 异或和 与 和 相同的区间的个数(如单个数, 2 ^ 4 == 2 + 4)
异或的原理是 对应位不同则为1, 反之为0, 使得两数异或要么等于两数和(2 ^ 4 == 010 ^ 100 == 110), 要么更少(3 ^ 2 == 11 ^ 10 == 01), 即 a ^ b <= a + b, 当且仅当对应二进制位数不同时等号成立, 究其原因是每个二进制位要么不变(一者为一另一者为零 或 两者皆为零), 要么减少(两者皆为一);
1. 整体思路是维护以每个 i 为开始的最大可行区间;
2. 根据异或的原理, 我们可以把每个数(如4 == 1000)中的 1 想象为一个木板, 只有当这个位置只存在一个木板时才能使sum1(区间和) == sum2(区间异或和)(若木板同位:1000 ^ 1100 == 0100, 使得异或和小于区间和);
3. sum2 ^= a[i], 每一次可以看作插拔相应位置木板;
4. 代码思想来自b站 董晓算法;
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define lowbit(x) (x & -(x))
#define INF 0x3f3f3f3f
const int N = 2e5 + 7;
ll sum1, sum2;
ll a[N];
int main()
{
IOS;
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int ans = 0;
for (int i = 1, j = 0; i <= n; ) {
while (j + 1 <= n && sum1 + a[j + 1] == (sum2 ^ a[j + 1])) {
j++;
sum1 += a[j];
sum2 ^= a[j];
}
ans += j - i + 1;
sum1 -= a[i];
sum2 ^= a[i];
i++;
}
cout << ans;
return 0;
}