1 双指针

1)统计子矩阵

指针要求单调性,第一题都是头指针,第二题是头尾指针,确定用哪个,要看怎样能保持单调性。我们不希望指针调头

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

2)整数小拼接

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

点击查看代码
#include <bits/stdc++.h> 
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
ll a[N];
string s[N], str;//s[i]表示转为字符串的 a[i];str表示转为字符串的 k 
ll n, k;
int cmp(string s1, string s2) {//字符串大小比较,不解释 
    if (s1.size() == s2.size()) { 
        if (s1 == s2) return 0;
        else if (s1 < s2) return 1;
        else return -1;
    }
    if (s1.size() < s2.size()) return 1;
    else return -1;
}
void init() {//初始化,不解释 
	cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) cin >> a[i];
    sort(a + 1, a + 1 + n);
    for (int i = 1; i <= n; i ++ ) s[i] = to_string(a[i]);
    str = to_string(k);
}
int main() {
	init();
    ll res = 0;
    //第一遍双指针 
    int l = 1, r = n;
    while(l <= r) {
        int t = cmp(s[l] + s[r], str);
        //l放前面,r放后面 
        if(t == 1) {//拼接后小于k 
            res += r - l;
            l ++;
        } else if(t == 0) {//拼接后等于k 
            res += r - l;
            l ++, r --;
        } else r --;//拼接后大于k 
    }
    //第二遍双指针 
    l = 1, r = n;
    while(l <= r) {
        int t = cmp(s[r] + s[l], str);//r放前面l放后面 
        if(t == 1) {//同第一遍
            res += r - l;
            l ++;
        } else if(t == 0) {
            res += r - l;
            l ++, r --;
        } else r --;  
    }
    cout << res;
    return 0;
}

2 滑动窗口/单调队列/单调栈

日志统计

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

普通做法
#include<bits/stdc++.h>
using namespace std;
struct o{
	int a,b;
}a[100005];
int n,d,k;
int cmp(o x,o y){
	if(x.b!=y.b)return x.b<y.b;
	return x.a<y.a;
}
int f1(int x, int y) {
    int r = x;  // 初始右端点为 x
    for (int i = x; i <= y; i++) {  // 遍历每个左端点 i
        // 扩展右端点 r 到最远位置,使得 a[r+1].a - a[i].a < d
        while (a[r + 1].a - a[i].a < d && r < y) { 
            r++;
        }
        // 窗口 [i, r] 的长度 ≥ k 则满足条件
        if (r - i + 1 >= k) return 1; 
    }
    return 0;  // 所有左端点都不满足条件
}
int main()
{
	cin>>n>>d>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i].a>>a[i].b;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++){
		int r=i;
		while(a[r+1].b==a[i].b)r++;//找到最后一个id相同的位置 
		if(f1(i,r)){//判断i到r可不可以是热帖 
			cout<<a[i].b<<endl;
		}
		i=r;
	}
	return 0;
}
单调队列 ``` #include using namespace std; int n, d, k; int l = 1, head, tail, que[100050];//l是这一段的开头 struct Zan { int ts, id; bool operator <(const Zan& a)const { if (id != a.id)return id < a.id; return ts < a.ts; }//当然可以用构造比较函数 cmp,但是个人更加喜欢重载运算符 }z[100050]; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//关闭流同步好习惯 cin >> n >> d >> k; for (int i = 1;i <= n;i++)cin >> z[i].ts >> z[i].id; sort(z + 1, z + n + 1); for (int i = 1;i <= n;i++) { while (z[l].id == z[i].id)i++; i--;//循环找到第一个不处于这一段的,此时i为下一段的开头,为了方便减去一 head = 1; tail = 0; memset(que, 0, sizeof(que));//每一段都要初始化 for (int j = l;j <= i;j++) { while (head <= tail && z[que[head]].ts + d <= z[j].ts)head++;//消除队头不满足的元素 que[++tail] = j;//进队 if (tail - head + 1 >= k) {//满足要求直接输出,因为已经按编号排好 cout << z[l].id << '\n'; l = i + 1;//更新端点,在下一个循环,i也会加一变成和l一样,在for循环开头的while循环一样可以找到末端点 break; } if (j == i)l = i + 1;//没有找到也要更新端点 }//单调队列模板 } return 0; } ```
posted on 2025-03-21 21:46  Hoshino1  阅读(9)  评论(0)    收藏  举报