双指针

入门/板题

P1638 逛画展

基础双指针 初始值需要设置为 \(l=1\)\(r=0\)

两种操作:

  1. \(tot=m\) 我们出队 在出队过程中统计答案

  2. \(tot\neq m\) 选择入队 直到 \(tot=m\)

先进行 \(1\) 操作 因为我们的终止条件是右端点 所以需要让操作右端点的操作 \(2\) 后操作 否则会导致统计到边界外的东西

注意移动指针时的边界条件

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , cnt[N] , ansl , ansr = inf , tot , a[N];
string s;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	int l = 1 , r = 0;
	while ( r <= n )
	{
		while ( tot == m && l <= r )
		{
			if ( r - l + 1 < ansr - ansl + 1 ) ansl = l , ansr = r;
			tot -= ( -- cnt[a[l++]] == 0 );
		}
	    tot += ( ++ cnt[a[++r]] == 1 );
	}
	cout << ansl << ' ' << ansr << endl;
	return 0;
}

P3143 [USACO16OPEN] Diamond Collector S

显然我们可以先将序列排序再用双指针来进行统计

但是因为有两个架子 所以我们需要从前往后和从后往前都扫一遍 最后枚举中间点并组合一下即可

注意 \(f\)\(g\) 数组都需要取得前缀最大值

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , k , f[N] , g[N] , l , r , a[N] , ans;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	a[n+1] = inf;
	sort ( a + 1 , a + n + 1 );
	l = 1 , r = 0;
	while ( r <= n )
	{
		while ( a[r] - a[l] > k && l <= r ) ++ l;
		f[r] = max ( f[r-1] , r - l + 1 ) , ++ r;
	}
	l = n + 1 , r = n;
	while ( l )
	{
		while ( a[r] - a[l] > k && l <= r ) -- r;
		g[l] = max ( g[l+1] , r - l + 1 ) , -- l;
	}
	for ( int i = 1 ; i < n ; i ++ ) ans = max ( ans , f[i] + g[i+1] );
	cout << ans << endl;
	return 0;
}

P8783 [蓝桥杯 2022 省 B] 统计子矩阵

简单双指针 我们枚举一个上边界和一个下边界 并用双指针和前缀和来统计上下边界之间 合法区间的个数

注意计入区间要用 \(r-l+1\) 而不能单纯将答案自增(因为我们双指针一次统计的是以 \(r\) 为左端点的合法区间个数)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
const int N = 500 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , k , a[N][N] , b[N] , ans;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) 
		for ( int j = 1 ; j <= m ; j ++ )
			a[i][j] = read() + a[i-1][j];
	for ( int i = 1 ; i <= n ; i ++ ) 
		for ( int j = i ; j <= n ; j ++ )
		{
			for ( int l = 1 ; l <= m ; l ++ ) b[l] = a[j][l] - a[i-1][l];
			int l = 1 , r = 0 , tot = 0;
			while ( r <= m )
			{
				while ( l <= r && tot > k ) tot -= b[l++];
				ans += r - l + 1 , tot += b[++r];
			}
		}
	cout << ans << endl;
	return 0;
}

P8472 [Aya Round 1 G] 咕噜论坛(post)

思想和 \(P8783\) 类似

注意在判断 \(g\)\(b\) 的时候要注意区间内有没有 \(p\) (因为有 \(p\) 是一定不能合法的)

代码虽然长但是一次 \(AC\) 赢!

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e3 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , k , sumb[N][N] , sumg[N][N] , sump[N][N] , ans;
int lx , ly , rx , ry;
char ansc;
string mp[N];

int checkb ( int lx , int ly , int rx , int ry )
{
	int sp = sump[rx][ry] - sump[lx-1][ry] - sump[rx][ly-1] + sump[lx-1][ly-1];
	int sg = sumg[rx][ry] - sumg[lx-1][ry] - sumg[rx][ly-1] + sumg[lx-1][ly-1];
	if ( sg <= k && !sp ) return 1;
	else return 0;
}

void solveb ( int i , int j )
{
	int l = 1 , r = 0;
	while ( r <= m )
	{
		while ( l <= r && !checkb(i,l,j,r) ) ++ l;
		if ( l <= r && ( r - l + 1 ) * ( j - i + 1 ) > ans ) ans = ( r - l + 1 ) * ( j - i + 1 ) , lx = i , ly = l , rx = j , ry = r , ansc = 'B';
		++ r;
	}
}

int checkg ( int lx , int ly , int rx , int ry )
{
	int sp = sump[rx][ry] - sump[lx-1][ry] - sump[rx][ly-1] + sump[lx-1][ly-1];
	int sb = sumb[rx][ry] - sumb[lx-1][ry] - sumb[rx][ly-1] + sumb[lx-1][ly-1];
	if ( sb <= k && !sp ) return 1;
	else return 0;
}

void solveg ( int i , int j )
{
	int l = 1 , r = 0;
	while ( r <= m )
	{
		while ( l <= r && !checkg(i,l,j,r) ) ++ l;
		if ( l <= r && ( r - l + 1 ) * ( j - i + 1 ) > ans ) ans = ( r - l + 1 ) * ( j - i + 1 ) , lx = i , ly = l , rx = j , ry = r , ansc = 'G';
		++ r;
	}
}

int checkp ( int lx , int ly , int rx , int ry )
{
	int sp = sump[rx][ry] - sump[lx-1][ry] - sump[rx][ly-1] + sump[lx-1][ly-1];
	return sp == ( rx - lx + 1 ) * ( ry - ly + 1 );
}

void solvep ( int i , int j )
{
	int l = 1 , r = 0;
	while ( r <= m )
	{
		while ( l <= r && !checkp(i,l,j,r) ) ++ l;
		if ( l <= r && ( r - l + 1 ) * ( j - i + 1 ) > ans ) ans = max ( ( r - l + 1 ) * ( j - i + 1 ) , ans ) , lx = i , ly = l , rx = j , ry = r , ansc = 'P';
		++ r;
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	for ( int i = 1 ; i <= n ; i ++ ) 
	{
		cin >> mp[i] , mp[i] = " " + mp[i];
		for ( int j = 1 ; j <= m ; j ++ )
		{
			sumb[i][j] = sumb[i-1][j] + sumb[i][j-1] - sumb[i-1][j-1] + ( mp[i][j] == 'B' );
			sumg[i][j] = sumg[i-1][j] + sumg[i][j-1] - sumg[i-1][j-1] + ( mp[i][j] == 'G' );
			sump[i][j] = sump[i-1][j] + sump[i][j-1] - sump[i-1][j-1] + ( mp[i][j] == 'P' );
		}
	}
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = i ; j <= n ; j ++ )
			solveb(i,j) , solveg(i,j) , solvep(i,j);
	cout << ans << endl;
	for ( int i = lx ; i <= rx ; i ++ )
		for ( int j = ly ; j <= ry ; j ++ )
			mp[i][j] = ansc;
	for ( int i = 1 ; i <= n ; i ++ , cout.put(endl) )
		for ( int j = 1 ; j <= m ; j ++ )
			cout << mp[i][j];
	return 0;
}

P8708 [蓝桥杯 2020 省 A1] 整数小拼接

双指针的题需要注意的最大问题:区间合法的判断条件

我们可以先将数组排序再尝试进行拼接

第一次我们先用 \(l\) 拼在 \(r\) 的前面

如果区间合法(指 \(a[l]a[r]\) 这个数 \(\le k\)) 那么显然 \(a[l]\) 可以和 \(a_i(i\in [l+1,r] )\) 中的所有数去匹配

那么我们再尝试增大 \(l\) 指针去匹配即可 注意计入区间的时候因为自己不能和自己匹配 所以不能 \(+1\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
const int N = 1e5 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int merge ( int x , int y )
{
	int res = 1 , temp = y;
	while ( temp ) res *= 10 , temp /= 10;
	return x * res + y;
}

int n , k , a[N] , ans;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , k = read();
	generate ( a + 1 , a + n + 1 , read );
	sort ( a + 1 , a + n + 1 );
	int l = 1 , r = n;
	while ( l != r )
	{
		while ( l < r && merge ( a[l] , a[r] ) <= k ) ans += r - l , l ++;
		if ( l == r ) break;
		-- r;
	}
	l = 1 , r = n;
	while ( l != r )
	{
		while ( l < r && merge ( a[r] , a[l] ) <= k ) ans += r - l , l ++;
		if ( l == r ) break;
		-- r;
	}
	cout << ans << endl;
	return 0;
}

P1381 单词背诵

基础双指针 注意区间合法条件为 \(tot=res\) 而不是 \(tot=n\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , ans = inf , res;

map<string,int> need , cnt , vis;
string str , s[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) cin >> str , need[str] = 1;
	m = read();
	for ( int i = 1 ; i <= m ; i ++ )
	{
		cin >> s[i];
		if ( need[s[i]] && !vis[s[i]] ) vis[s[i]] = 1 , ++ res;
	}
	if ( res == 0 ) return cout << 0 << endl << 0 << endl , 0;
	int l = 1 , r = 0 , tot = 0;
	while ( r <= m )
	{
		while ( l <= r && tot == res ) ans = min ( ans , r - l + 1 ) , tot -= need[s[l]] ? ( -- cnt[s[l]] == 0 ) : 0 , ++ l;
		++ r , tot += need[s[r]] ? ( ++ cnt[s[r]] == 1 ) : 0;
	}
	cout << res << endl << ans << endl;
	return 0;
}

P3029 [USACO11NOV] Cow Lineup S

和上一道题没什么区别 只是需要离散化

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , ans = inf , cnt[N] , res , vis[N];

vector<int> lsh;
struct node { int x , id; } a[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].x = read() , a[i].id = read() , lsh.eb(a[i].id);
	sort ( lsh.begin() , lsh.end() );
	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
	for ( int i = 1 ; i <= n ; i ++ ) a[i].id = lower_bound ( lsh.begin() , lsh.end() , a[i].id ) - lsh.begin() + 1 , res += !vis[a[i].id] , vis[a[i].id] ++;
	sort ( a + 1 , a + n + 1 , [](const node &a , const node &b) { return a.x < b.x; } );
	int l = 1 , r = 0 , tot = 0;
	while ( r <= n )
	{
		while ( l <= r && tot == res ) ans = min ( a[r].x - a[l].x , ans ) , tot -= ( -- cnt[a[l++].id] == 0 );
		tot += ( ++ cnt[a[++r].id] == 1 );
	}
	cout << ans << endl;
	return 0;
}

P8775 [蓝桥杯 2022 省 A] 青蛙过河

转化题意 对于所有长度 \(= ans\) 的区间 都需要让区间和 \(\ge 2x\) 求这个最小的 \(ans\)

因为如果最小跳跃长度为 \(ans\) 那么所有长度为 \(ans\) 的区间内 每次石头的高度都会有一个 \(-1\) 所以显然需要让每一个区间和都 \(\ge 2x\)

所以我们对于每一个 \(r\) 需要求出最短的符合条件( 区间和 \(\ge 2x\) )的 \(l\) 来取 \(max\) 进行统计

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e5 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , x , a[N] , tot , ans;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , x = read();
	for ( int i = 1 ; i < n ; i ++ ) a[i] = read();
	int l = 1 , r = 0 , tot = 0;
	while ( r < n )
	{
		while ( l <= r && tot >= 2 * x ) tot -= a[l++];
		tot += a[++r];
		ans = max ( ans , r - l + 1 );
	}
	cout << ans << endl;
	return 0;
}

进阶/转化

Andrey and Escape from Capygrad

对于线段的双指针入门题

可以发现对于在 \([l,r]\) 区间的一次询问 要么询问本身呆在原地不动 要么向后跳到 \(b\)

因为跳到的最远点具有单调性 那么我们需要将询问离线排序并用双指针进行统计

\(PS:\) 具有单调性的原因:

假设一个 \(x'>x\)\(x\) 能到达的地方比 \(x'\)

那么一定存在一个区间 \([l,r](r<x')\) 使得 \(x\) 可以跳到更远的地方 所以此时的 \(b_i\) 一定大于 \(r\)\(b_i\le r\) 矛盾

\(upd\ on\ 10.30\): 貌似并不是很传统的双指针()

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 2e5 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , qq , ans[N];

struct edge { int l , r , a , b; } a[N];
struct query { int pos , id; } q[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		n = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].a = read() , a[i].b = read();
		qq = read();
		for ( int i = 1 ; i <= qq ; i ++ ) q[i].pos = read() , q[i].id = i;
		sort ( a + 1 , a + n + 1 , [](const edge &a , const edge &b) { return a.l < b.l; } );
		sort ( q + 1 , q + qq + 1 , [](const query &a , const query &b) { return a.pos < b.pos; } );
		int pos = 0 , now = 1;
		for ( int i = 1 ; i <= qq ; i ++ )
		{
			pos = max ( pos , q[i].pos );
			while ( a[now].l <= pos && now <= n ) pos = max ( pos , a[now].b ) , ++ now;
			ans[q[i].id] = pos;
		}
		for ( int i = 1 ; i <= qq ; i ++ ) cout << ans[i] << ' ';
		cout << endl;
	}
	return 0;
}

Cyclic Rotation

玄幻奇妙的题 思想是双指针

我们发现转 \(a\) 比较难 但是我们可以从终止状态 \(b\) 回推

从后往前贪心地匹配 如果遇到了一个不能匹配的点 那么如果 \(b[i]=b[i+1]\) 那么这两个点可以成为一个循环的起始和末尾 用 \(map\) 记录一下

然后继续贪心匹配 如果又遇到了一个不能匹配的点 如果 \(map\) 中有这个数 \(a[i]\) 那么相当于 \(b[i]\) 为起始的这个循环可以转回来 那么我们清除贡献即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 3e5 + 5;
const int inf = 0x3f3f3f3f;

int read()
{ 
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , a[N] , b[N];
map<int,int> cnt;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		cnt.clear();
		int flag = 1;
		n = read(); 
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 ; i <= n ; i ++ ) b[i] = read();
		int posa = n , posb = n;
		b[n+1]=0;
		while ( posb )
		{
			while ( posa && posb && a[posa] == b[posb] ) -- posa , -- posb;
			// cout << posa << ' ' << posb << endl;
			if ( posa == 0 || posb == 0 ) break;
			if ( b[posb] == b[posb+1] ) cnt[b[posb--]] ++;
			else if ( cnt[a[posa]] ) cnt[a[posa--]] --;
			else { flag = 0; break; }
		}
		cout << ( flag ? "YES" : "NO" ) << endl;
	}
	return 0;
}

Integers Have Friends

转化题意:\([l,r]\) 合法当且仅当差分数组 \([l,r-1]\) 合法

证明:设 \(a_l\bmod m=a_{l+1}\bmod m=\dots=a_r\bmod m=x\)

那么有

  • \(a_l-x=k_l\times m\)
  • \(a_{l+1}-x=k_{l+1}\times m\)
  • \(a_r-x=k_r\times m\)

也就是 \(a_l,a_{l+1},\dots,a_r\) 之间相差一定是 \(m\) 的整数倍

那么我们用 \(st\) 表维护区间 \(gcd\) 并双指针处理即可

注意记录答案时的 \(r-l+2\) 处理 因为我们 \([l,r]\) 合法当且仅当差分数组 \([l,r-1]\) 合法 所以在差分数组上进行操作的时候需要加一

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long 
const int N = 2e5 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , st[N][31] , maxx , a[N];

int query ( int l , int r )
{
	int len = __lg ( r - l + 1 );
	return __gcd ( st[l][len] , st[r-(1<<len)+1][len] );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		maxx = 0;
		n = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 ; i < n ; i ++ ) st[i][0] = abs ( a[i+1] - a[i] );
		for ( int j = 1 ; j <= 25 ; j ++ ) 
			for ( int i = 1 ; i + ( 1 << j ) - 1 < n ; i ++ )
				st[i][j] = __gcd ( st[i][j-1] , st[i+(1<<j-1)][j-1] );
		int l = 1 , r = 0;
		while ( r < n )
		{
			while ( l <= r && query ( l , r ) == 1 ) l ++; 
			maxx = max ( maxx , r - l + 2 ) , r ++;
			//差分合法性见第一页最后一篇 
			//这里不是 r-l+1 的原因是 如果 [l,r] 区间合法 那么显然[l,r+1] 区间都是合法的(因为我们记录的是差分 
		}
 		cout << maxx << endl;
	}	
	return 0;
}

P6465 [传智杯 #2 决赛] 课程安排

双指针确实十分多变()

先不考虑 \(l\) 的限制 看如何求出头尾不同 相邻不同的序列

先看相邻不同的限制 显然对于每一个 \(l\) 端点 离它最远的 \(r\) 端点都是单调不降的 那么我们考虑枚举这个 \(l\)

不能枚举 \(r\) 个人认为是对于 \(r\) 左端点 \(l\) 可能移动很大 比如 \(a[r]=a[r-1]\)

再看头尾不同的限制 我们维护一个桶 存储区间内某个 \(a_i\) 的出现次数 那么对于一个 \(l\) 合法区间即为 \(r-l+1-cnt_{a_l}-1\) (后面 \(-1\) 是因为自己不能和自己匹配)

再考虑 \(l\) 的限制 实质上是限制序列长度在 \([l,n]\) 之间 我们可以将长度为 \([1,l-1]\)\([1,n]\) 的答案都算出来 作差即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
const int N = 1e6 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , lim , a[N] , l , r , tot , cnt[N] , res1 , res2; 

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		res1 = res2 = 0;
		n = read() , lim = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , cnt[i] = 0;
		l = 1 , r = 0;
		while ( l <= n )
		{
			while ( l > r || r < n && a[r] != a[r+1] ) ++ cnt[a[++r]];
			-- cnt[a[l]] , res1 += r - l + 1 - cnt[a[l]];
			++ l;
		}
		l = 1 , r = 0;
		while ( l <= n )
		{
			while ( l > r || r < n && a[r] != a[r+1] && r - l + 1 < lim - 1 ) ++ cnt[a[++r]];
			-- cnt[a[l]] , res2 += r - l + 1 - cnt[a[l]];
			++ l;
		}
		cout << res1 - res2 << endl;
	}
	return 0;
}

P1712 [NOI2016] 区间

双指针+线段树的转化

我们显然可以将区间按照长度排序来方便统计(因为题目中要求是最长的区间长度差值)

那么我们可以用双指针维护区间的选取 并用线段树维护覆盖的合法性

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , tot;

struct Tree
{
	struct node { int maxx , add; } t[N<<2];
	inl void pushmaxx ( int p , int val ) { t[p].maxx += val , t[p].add += val; }
	inl void down ( int p ) { if ( t[p].add ) pushmaxx ( ls , t[p].add ) , pushmaxx ( rs, t[p].add ) , t[p].add = 0; }
	inl void up ( int p ) { t[p].maxx = max ( t[ls].maxx , t[rs].maxx ); }
	void upd ( int p , int l , int r , int x , int y , int val ) 
	{
		if ( x == 0 && y == 0 ) return;
		if ( x <= l && r <= y ) return pushmaxx ( p , val );
		down(p);
		if ( x <= mid ) upd ( lson , x , y , val );
		if ( mid + 1 <= y ) upd ( rson , x , y , val );
		up(p);
	}
}T;

vector<int> lsh;
struct edge { int l , r , len; } a[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , lsh.eb(a[i].l) , lsh.eb(a[i].r);
	sort ( lsh.begin() , lsh.end() );
	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
	tot = lsh.size();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].len = a[i].r - a[i].l , a[i].l = lower_bound ( lsh.begin() , lsh.end() , a[i].l ) - lsh.begin() + 1 , a[i].r = lower_bound ( lsh.begin() , lsh.end() , a[i].r ) - lsh.begin() + 1;
	sort ( a + 1 , a + n + 1 , [](const edge &a , const edge &b) { return a.len < b.len; } );
	int l = 1 , r = 0 , ans = inf;
	while ( r <= n )
	{
		while ( l <= r && T.t[1].maxx == m ) ans = min ( ans , a[r].len - a[l].len ) , T.upd ( 1 , 1 , tot , a[l].l , a[l].r , -1 ) , l ++;
		++ r , T.upd ( 1 , 1 , tot , a[r].l , a[r].r , 1 );
	}
	cout << ( ans == inf ? -1 : ans ) << endl;
	return 0;
}

Boring Segments

和上一题几乎一样 不同的是我们这次按照区间权值排序 而且因为需要保证两个区间之间有交集 那么区间端点需要 \(-1\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
#define int long long
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , tot , ans = inf;

struct edge { int l , r , w; } a[N];

struct Tree
{
	struct node { int minn , add; } t[N<<2];
	inl void pushminn ( int p , int val ) { t[p].add += val , t[p].minn += val; }
	inl void down ( int p ) { if ( t[p].add ) pushminn ( ls , t[p].add ) , pushminn ( rs , t[p].add ) , t[p].add = 0; }
	inl void up ( int p ) { t[p].minn = min ( t[ls].minn , t[rs].minn ); }
	void upd ( int p , int l , int r , int x , int y , int val )
	{
		if ( x <= 0 && y <= 0 ) return;
		// cout << p << ' ' << l << ' ' << r << ' ' << x << ' ' << y << ' ' << val << endl;
		if ( x <= l && r <= y ) return pushminn ( p , val );
		down(p);
		if ( x <= mid ) upd ( lson , x , y , val );
		if ( mid + 1 <= y ) upd ( rson , x , y , val );
		up(p);
	}
	int query ( int p , int l , int r , int x , int y )
	{
		if ( x <= l && r <= y ) return t[p].minn;
		int res = inf; down(p);
		if ( x <= mid ) res = min ( res , query ( lson , x , y ) );
		if ( mid + 1 <= y ) res = min ( res , query ( rson , x , y ) );
		return res;
	}
}T;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].w = read();
	sort ( a + 1 , a + n + 1 , [](const edge &a , const edge &b) { return a.w < b.w; } );
	int l = 1 , r = 0;
	while ( r <= n ) 
	{
		while ( l <= r && T.query ( 1 , 1 , m - 1 , 1 , m - 1 ) ) ans = min ( ans , a[r].w - a[l].w ) , T.upd ( 1 , 1 , m - 1 , a[l].l , a[l].r - 1 , -1 ) , l ++;
		++ r , T.upd ( 1 , 1 , m - 1 , a[r].l , a[r].r - 1 , 1 );
	}
	cout << ans << endl;
	return 0;
}

Quiz Master

前置知识:\(b\)\(a\) 整除指的是 \(b\bmod a = 0\)

那么我们对于每一个数 求出它的因数 并开桶来统计 \(1-m\) 中的因数访问情况

双指针统计即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+r>>1)
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e5 + 5;
const int maxn = 1e5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , cnt[N] , a[N] , ans = inf;

vector<int> vec[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	for ( int i = 1 ; i <= maxn ; i ++ )
		for ( int j = i ; j <= maxn ; j += i )
			vec[j].eb(i);
	int T = read();
	while ( T -- )
	{
		n = read() , m = read();
		ans = inf;
		for ( int i = 1 ; i <= m ; i ++ ) cnt[i] = 0;
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		sort ( a + 1 , a + n + 1 );
		int l = 1 , r = 0 , tot = 0;
		while ( r <= n )
		{
			while ( l <= r && tot == m ) 
			{
				ans = min ( ans , a[r] - a[l] );
				for ( auto v : vec[a[l]] ) if ( v <= m ) tot -= ( -- cnt[v] == 0 );
				++ l;
			}
			++ r;
			for ( auto v : vec[a[r]] ) if ( v <= m ) tot += ( ++ cnt[v] == 1 );
		}
		cout << ( ans == inf ? -1 : ans ) << endl;
	}
	return 0;
}

Glider

大家写双指针好像都喜欢移动 \(l\) 指针,那就来一篇移动 \(r\) 指针的题解。

对于本题,移动区间显然有单调性,因为走越多的区间,高度一定不会上升,那么可以采用双指针来统计。

我们设 \(ll_i\) 为一段上升气流区间的左端点,\(rr_i\) 为右端点,\(l\) 为左指针,\(r\) 为右指针,\(tot\) 为当前的高度。

那么初始条件为 \(l=r=1\)\(tot=m\)

当当前高度 \(\le0\) 的时候,尝试移动左指针,使得区间合法( \(tot\ge 0\) )。

否则计入答案并移动右指针,注意在统计完区间之后还要加上剩余的单调下降高度 \(tot\)

#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define eb emplace_back
#define pb pop_back
#define endl '\n'
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define int long long
const int N = 2e5 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , ll[N] , rr[N] , ans;

vector<int> vec[N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) ll[i] = read() , rr[i] = read();
	int l = 1 , r = 1 , tot = m;
	while ( r <= n )
	{
		while ( l <= r && tot <= 0 ) tot += ll[l+1] - rr[l], ++ l;
		ans = max ( ans , rr[r] - ll[l] + tot );
		tot -= ll[r+1] - rr[r]; ++ r;
	}
	cout << ans << endl;
	return 0;
}

posted @ 2023-10-27 19:13  Echo_Long  阅读(33)  评论(0编辑  收藏  举报