刷题周记(四.1)——#单调栈:模板题#队列:机器、Blah数集#双端队列:滑动窗口#并查集:How Many Tables、HowManyAnswersAreWrong、食物链、连通块中点的数

——2020年11月15日(周日)——————————————————

#单调栈

一、模板单调栈

链接

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6 + 10;
int a[N], b[N];
int main(){
	stack<int> s;
	int n; cin >> n;
	for(int i = 1; i <= n; i ++){
		scanf("%d", &a[i]);
		while(!s.empty() && a[i] > a[s.top()]){
			b[s.top()] = i;
			s.pop();
		}
		s.push(i); 
	}
	for(int i = 1;i <= n; i ++) 
		printf("%d ", b[i]);
	return 0;
}

递减单调栈……

#队列

二、机器翻译

队列……
链接

#include<iostream>
#include<queue>
using namespace std;
bool vis[1010];
int main()
{
	queue<int>q;
	int m,n;
	cin>>m>>n;//内存容量和文章长度
	int cnt=0,a,num=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a;
		//有翻译记录就跳过
		if(vis[a])continue;
		//没有翻译记录时 
		//查找次数加1
		cnt++;
		//满了的入队列的操作 
		if(num==m)
		{
			//首先清除记录 
			vis[q.front()]=0;
			//去掉队列头
			q.pop(); 
			//加入队列尾
			q.push(a); 
			vis[a]=1; 
		}
		//没满队列时的操作 
		else
		{
			//添加一个记录 
			vis[a]=1;
			//加入队列尾 
			q.push(a);
			//队列元素的数量 	
			num++;
		}
	}
	cout<<cnt<<endl;
	return 0;
}

三、Blah数集

链接

#include<iostream>
#include<queue>
using namespace std;
int a,n,k;
int main(){
	while(cin>>a>>n)
	{
		//两个队列 一个装2a+1 一个装3a+1 
		queue<int> q1,q2;
		k=1;
		//确定第n位的操作 
		while(k<n)
		{
			q1.push(a*2+1);//入队列操作 
			q2.push(a*3+1);//
			//循环替换a的值 
			//有点像归并排序合并时那种二分的思想
			//反正就是两个单调递增的队列,其中一个的队头一定是两个队列中最小的数
			//由题意得,我们整个序列是单调递增的,但是我们不需要合并,只要一只保留两个队列就好了
			if(q1.front()<q2.front()){
				a=q1.front();
				q1.pop();
			}
			else if(q1.front()>q2.front()){
				a=q2.front();
				q2.pop();
			}
			//这是两者相同的情况,由于整个序列其实是没有重复元素的,所以要同时弹出队头
			else{
				a=q1.front();
				q1.pop();
				q2.pop();
			}
			//对应要加1 
			k++;
		}
		//确定好后就输出
		cout<<a<<endl; 
	}
	return 0;
}

总之,这些容器就是这么用的啦!

#双端队列(容器)

详情请点击这里

四、滑动窗口(这次是用deque做哒!)

四刷了哦,滑动窗口这道题……
以下是四种定义deque的方法

// 直接定义
std::deque<int> first;                                // empty deque of ints
//填充数字
  std::deque<int> second (4,100);                       // four ints with value 100
  //The contents of second are: 100 100 100 100
//复制某双端队列的某一段
  std::deque<int> third (second.begin(),second.end());  // iterating through second
  //The contents of third are: 100 100 100 100
//或者直接复制下整个其它双端队列
  std::deque<int> fourth (third);                       // a copy of third
  //The contents of fourth are: 100 100 100 100
//或者复制某个数组
  // the iterator constructor can be used to copy arrays:
  int myints[] = {16,2,77,29};
  std::deque<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
//The contents of fifth are: 16 2 77 29
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
//不要给双端队列q定义大小,我也不清楚
deque<int>q;
int main(){
    //个数,窗口大小
    int n, k; cin >> n >> k;
    for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
    //递增
    //依次将数组元素入队
    for(int i = 1; i <= n; i ++){
        //首先是出队操作
        while( !q.empty() && a[i] < a[q.back()]) q.pop_back();
        q.push_back(i);
        if(!q.empty() && q.front() < i - k + 1) q.pop_front();
        if(!q.empty() && i >= k) printf("%d ", a[q.front()]);
    }
    puts("");
    //递减
    q.clear();
    for(int i = 1; i <= n; i ++){
        //首先是出队操作
        while( !q.empty() && a[i] > a[q.back()]) q.pop_back();
        q.push_back(i);
        if(!q.empty() && q.front() < i - k + 1) q.pop_front();
        if(!q.empty() && i >= k) printf("%d ", a[q.front()]);
    }
    puts("");
    
    return 0;
}

——2020年11月16日(周一)——————————————————

#并查集

今天的任务是并查集!
这是过去的笔记哒!
还有一个笔记

一、How Many Tables (几张桌子)

这是题目哒!
仔细分析,这就是模板题一道而已啦。

//POJ不能用万能头,一时间不习惯hh
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 1e3 + 10;
int n, m, P;
int p[N];

int find(int x){
	//这个写法有个名字,叫做路径压缩,可以减少时间。写法也很简单,很容易理解。
	return p[x] == x ? p[x] : p[x] = find(p[x]);
}

int main(){
//	freopen("ttt.in", "r", stdin);
//	freopen("ttt.out", "w", stdout);
	int t;cin >> t;
	while(t --){
		int n, m;
	    scanf("%d%d", &n, &m);
		int cnt = n;
	    //一开始自己是自己的父节点 
	    for (int i = 1; i <= n; i ++ ) p[i] = i;
		//关系输入 
	    while (m -- )
	    {
	    	int a, b;
	        scanf("%d%d", &a, &b);
	        //将两个连同块连在一起 
	        //根据题目需求,要计算有几个连通块;
	        //这里find在搜过一次后,会直接将整个连通块的父节点都赋值为根节点
			//但是 p[find(a)] = find(b);之后a所在连通块所有的节点(除了上一个根节点)的父节点还是上一个根节点,并不是当前根节点。 
			//这里,要是不是一个连通块的,合并,那么就少一个连通块了哦。 
	        if(p[find(a)] != find(b)) cnt --;
			p[find(a)] = find(b);
	    }
	cout << cnt << endl;
	}
	return 0;
	
}

——2020年11月17日(周二)——————————————————

一、How Many Answers Are Wrong

How Many Answers Are Wrong
这道题从昨晚一直做到现在,其原因有两个
一是本蒟蒻巨弱;
二就是被一个奇奇怪怪的例子给误导了……
去网上看了一大堆没用的博客,其解析真的是不清不楚……果然大佬都是一遍过,根本不需要这么多解释。
还好有这位巨老的博客给我答疑解惑,我才能走出来。
在这里插入图片描述

看这里,加入输入的是(4, 8, 10)的话,说明从格子内的4开始一直到8这个红色的方块就是10了,
用前缀和数组sum来计算就是sum[8] - sum[3];这里sum[n]中的n是下标,也就是 蓝色方框 中的数字。

#include <iostream>
#include <cstdio>
using namespace std;
 //父节点 和 伪前缀和节点
int fa[200015], sum[200015];
//就是find()啦 
int father(int x){
	//找到了最大的爸爸 ,返回爸爸 
	if (x == fa[x]) return x;
	//没找到最大的爸爸,那就继续找 
	else {
		//这里用t来记录当前点的爸爸 
		int t = fa[x];
		//然后重新认最大的爸爸做爸爸 ,这就叫路径压缩! 
		fa[x] = father(fa[x]);
		//如果fa[x]不是祖先(最大的爸爸)节点就加上之前那个爸爸到最大的爸爸的距离
		//是祖先节点则sum[t]为零,毕竟最大的爸爸的爸爸就是自己,自己和自己难道还有距离吗?(笑) 
		sum[x] += sum[t];
		//然后就是返回这个最大的爸爸啦! 
		return fa[x];
	}
}
 
int main()
{
//	freopen("in.in" , "r" , stdin); 
	int n,m,a,b,c,r1,r2; 
	//如果输入不停 …… 
	while (scanf("%d%d" , &n, &m)!=EOF){
		for (int i = 0; i <= n ; i++) {
			fa[i] = i;
			sum[i] = 0;
		}
		int ans=0;
		for (int i = 0; i < m; i++){
			scanf("%d%d%d", &a, &b, &c);
			//这就要用前缀和来理解啦,因为是闭区间,所以左右端点都要取
			//如果难理解的话,就想想前缀和,在前缀和中我们取[a, b]之间的值的时候不也是s[b] - s[a - 1]吗?
			//这样就把a也算进来了。 
			a --;
			//找爸爸 
			r1 = father(a);
			r2 = father(b);
			//不是同一个爸爸,那就不是同一个家里面的人。
			//这里注意了,这里的点并不是按照顺序来的,也就是说1到6距离是300,而1到2距离是900这样也是成立的!
			//点号不代表序号!我就是被学案上的例题误解了! 
			if (r1 != r2){
				fa[r1] = r2;
				sum[r1] = -sum[a] + sum[b] + c;
			}
			else if (sum[a] != sum[b] + c) ans ++;
			
		}
		printf("%d
", ans);
	}
	return 0;
}

二、食物链

既然都做了一道带权并查集了,那就顺便把这道有趣的题目也复习一下吧

题目

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 5e5 + 10;
int sum;
int n, m, P;
int p[N];
int d[N];
//这里每执行一次都会直接将祖宗节点作为所有在内节点的父节点,
//其距离不会重复计算第二次,因为下一次就遍历不到之前的父节点了.
int find(int x){
	if(x != p[x]){
		int t = find(p[x]);
		d[x] += d[p[x]];
		p[x] = t;
	}
	return p[x];
}

int main(){
// 	freopen("ttt.in", "r", stdin);
// 	freopen("ttt.out", "w", stdout);	
	int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    while (k -- ){
    	int z, a, b;
        scanf("%d%d%d", &z, &a, &b);
        int pa = find(a), pb = find(b);
        //假话
        if(a > n || b > n) sum ++;
        else{
        	//1代表同类
            if (z == 1){
	            //d[a] - d[b]) % 3这里如果不是0就说明他们根本就不是同类的
            	if(pa == pb && (d[a] - d[b]) % 3 ) sum ++;
            	//这里用else if 是因为上面的if有两个判断句,
            	//用else 的话就是肯定了其它三种情况,
            	//然而我们需要处理的只是四种中的两种
            	else if(pa != pb){
            		p[pa] = pb;
            		//添加的时候由于还没有用到find()函数,所以并不会立即更新新连通块内所有点到其对应根节点的距离
            		//这时候就要先将之前的根节点到当前根节点的距离处理一下,
            		//下一次的find()就会更新新连通块内所有点到其根节点的距离了
            		d[pa] = d[b] - d[a];
            	} 
            	
            }
            //2代表a吃b
            else
            {
	            //假话,说反了
            	if(pa == pb && (d[a] - d[b] - 1) % 3) sum ++;
            	else if(pa != pb){  
            		p[pa] = pb;
    	       		d[pa] = d[b] + 1 - d[a];
            	}
            }
            
        }
        
    }
    cout << sum;
	return 0;
	
}

三、连通块中点的数量

现在看看还是很简单的一道题,不是吗?
AcWing 837. 连通块中点的数量

#include <iostream>
using namespace std;

const int N = 100010;

int p[N];
int sum[N];
int a, b;
string op;

int find(int x){
    return p[x] == x ? p[x] : p[x] = find(p[x]);
}

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i, sum[i] = 1;

    while (m -- )
    {
        cin >> op;
        if(op == "C"){
            scanf("%d%d", &a, &b);
            if(a != b){
                int pa = find(a), pb = find(b);
                if(pa != pb) sum[pb] += sum[pa], p[pa] = pb;
            }
        }
        
        else{
            if(op == "Q1"){
                scanf("%d%d", &a, &b);
                if(find(a) == find(b)) printf("Yes
");
                else printf("No
");
            }
            else scanf("%d", &a), printf("%d
", sum[find(a)]);
        }
        
    }

    return 0;
}

——(未完,但是标题不够写了……)————————————————

四.2的链接:点这里

原文地址:https://www.cnblogs.com/yuanyulin/p/14026710.html