双指针(尺取法)

 从裸题开始:

连续自然数(Tzoj 8514):

         t

 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;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值