• 刷题周记(四.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的链接:点这里

  • 相关阅读:
    JS_0051:JS GET 请求 获取 url 返回的参数 ; 模拟延时效果 ; 判断手机电脑并跳转
    H50081:背景音乐按钮创建 html元素上禁止拖拽
    CSS0026: div 内 多元素 水平 垂直 均匀 居中 显示
    JS_0056:JS 判断页面加载完成
    JS_0050:JS 字符串加密 解密
    JS_0054:JS 动态设置字体大小 JS 判断浏览器是否联网
    Ajax0007: ajax 同步发送 GET 请求
    JS_0052:JS 禁止触摸屏双指放大,双指缩放。
    移动端调试工具vConsole
    使用安卓 studio 更换app图标
  • 原文地址:https://www.cnblogs.com/yuanyulin/p/14026710.html
Copyright © 2020-2023  润新知