23寒假集训二1月3号(单调队列+倍增)

vjudge上面的题当天是我负责讲题所以写了一下博客,优先队列永远的敌人,一直没太整清楚

前置知识

倍增

//倍增
//给定一个数列,共有n个正数,现在有m次询问,每次询问给出一个t,求满足最小的k使得从第一个数到第k个数之和小于等于t;
#include<cstdio>
#include<iostream>

using namespace std;
const int maxn =1e5+10; 
int n,m;
int a[maxn];
int main()
{
 	cin >>n>>m;
 	for(int i=1;i<=n;i++)
 	{
  		cin >>a[i];
  		a[i] += a[i-1];
 	}
 	while(m--)
 	{
  		int t;
  		cin >>t;
  		int k=0,p=1;
  		while(p)
  		{
   			if(k+p>n)break;
   			if(a[k+p] <= t)k+=p,p*=2;
   			else p/=2;
  		}
    	cout <<k<<endl;
 	}
 return 0;
 } 

A - 切蛋糕

思路

推方程

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int sum[N], dq[N]; //dq表示模拟的一个双端队列,存的是坐标
int ans = -0x3fff;

int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1, x; i <= n; i++) {
		scanf("%d", &x);
		sum[i] = sum[i - 1] + x;
	}
	int l = 1, r = 1;
	for (int i = 1; i <= n; i++) { //枚举i
		//更新答案
		while (dq[l] < i - m) {
			l++;
		}
		ans = max(ans, sum[i] - sum[dq[l]]);
		//如果存在递减的情况就删掉
		while (l <= r && sum[dq[r]] >= sum[i]) {
			r--;
		}
		dq[++r] = i;
	}
	printf("%d\n", ans);
	return 0;
}


B - 好消息,坏消息

思路

因为是一个环状的,所以扩大一倍数组 用sum[q[l]] - sum[k - 1] >= 0 表示没有<0的情况 -3 5 1 2 -3 5 12

-3 5 1 2 -3 5 1 2

-3 2 3 5 2 7 8 10

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 5;
int a[N], sum[N], q[N];

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
		a[i + n] = a[i];
	}
	int  m = n << 1;
	for (int i = 1; i <= m; i++) {
		sum[i] = sum[i - 1] + a[i];
	}
	int l = 1, r = 1, ans = 0;
	q[1] = 1;
	for (int i = 1; i <= m - 1; i++) {
		while (q[l] < i - n + 1)
			l++; //让头子在区间内
		//存在递增的情况
		while (l <= r && sum[i] <= sum[q[r]])
			r--;
		q[++r] = i;
		int k = i - n + 1;
		if (k > 0 && sum[q[l]] - sum[k - 1] >= 0) {
			ans++;
		}
	}
	printf("%d\n", ans);
	return 0;
}

C - 良好的感觉

思路

枚举i,ans[i]表示以a[i]为最小的子段的最大和

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
long long a[N], sum[N], ans[N], q[N];
long long Anss;

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		sum[i] = sum[i - 1] + a[i];
	}
	int r = 0;
	q[r] = 0;
    //3 1 
    //q : 3 1
	for (int i = 1; i <= n; i++) {
		//查看是否可以更新之前的决策
		while (a[q[r]] >= a[i]) {
			ans[q[r]] += sum[i - 1] - sum[q[r]];
			r--;
		}
		//加入决策
		ans[i] = sum[i] - sum[q[r]];
		q[++r] = i;
	}
	for (int i = 1; i <= n; i++) {
		Anss = max(Anss, (long long)ans[i] * a[i]);
	}
	printf("%lld\n", Anss);
	return 0;
}

D - 机器翻译

思路

简单模拟

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
deque<int> dq;
int a[N];
bool inq[N];

int main() {
	int m, n;
	scanf("%d%d", &m, &n);
	int ans = 0;
	for (int i = 1, x; i <= n; i++) {
		scanf("%d", &x);
		if (inq[x])
			continue;
		ans++;
		if (dq.size() < m) {
			inq[x] = 1;
			dq.push_back(x);
		} else {
			inq[dq.front()] = 0;
			dq.pop_front();
			dq.push_back(x);
			inq[x] = 1;
		}
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2023-01-10 22:03  oddpointa  阅读(35)  评论(0)    收藏  举报