IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 2021-09-22 -> 正文阅读

[数据结构与算法]2021-09-22

20210922 题解

crf的noip模拟赛day1
在这里插入图片描述

T1:
在这里插入图片描述
题解:dp
设dp[i][j]表示右下角为(i,j)时最大正方形
初值:就是输入的那个矩阵。
转移:
if(dp[i][j]==0) continue;//什么也不干
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1;
下面这个东西什么意思?
min(dp[i][j-1],dp[i-1][j-1]表示从这个右下角向左拓展
的最长距离
min(dp[i-1][j],dp[i-1][j-1]表示从这个右下角向上拓展
的最长距离。
至于为什么?反证法,如果上限不如上述所言,
与dp的定义矛盾
由于求的是正方形,所以二者取小。

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
il int read()
{
	int s=0,w=1;char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') w=-1;c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<1)+(s<<3)+c-'0';c=getchar();
	}return s*w;
}
const int N=2010;
int n,m,c[N][N],dp[N][N],maxx;
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=n;i++) for(re int j=1;j<=m;j++) dp[i][j]=read();
	maxx=0;
	for(re int i=1;i<=n;i++){
		for(re int j=1;j<=m;j++){
			if(dp[i][j]==0) continue;
			dp[i][j]=min( min(dp[i-1][j],dp[i][j-1]), dp[i-1][j-1])+1;
			maxx=max(maxx,dp[i][j]);
		}
	}
	printf("%d",maxx);
	return 0;
}

T2:
在这里插入图片描述
先按照x,y单调递增双关键字排序
此题最小划分数=最长反链,也就是最长下降子序列
我们设每一排书为一组,每个组里最大值为组里最
后一个数(记为t[i])。
有这样的结论:一定有一种情况,
使得t[i]中i递增,t[i]递减
就是最大值下标递增,数值递减

因为当我们使数列单调上升后,从后往前贪心地
将每一个数字接在第一个数值大于它的数字的后面,
一定可以构造出一个最优解。此时,
所有不能接在前面数字之后的数字,必定构成
下降序列,而组数(设为h)就是该下降序列的长度。
由于下降序列长度不可能长于最长下降子序列
于是h>=f
同时,由于最长下降子序列的各个数字不能在同一组
里,于是至少分h组,h<=f
所以h=f
上面的结论得证

我感觉数据错了……所以这道题不放代码了
T3:
在这里插入图片描述
题干啰里巴嗦一大堆话,就最后一段有用……
70分:莫队暴力

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
il int read()
{
	int s=0,w=1;char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') w=-1;c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<1)+(s<<3)+c-'0';c=getchar();
	}return s*w;
}
const int N=1000010;
int n,m,a[N];
int Block,num[N],summ,ans[N];
struct node{
	int l,r,id;
}Q[N];
/*
m表示询问数量
*/ 
il bool cmp(node c,node d){
	if(c.l/Block != d.l/Block) return (c.l/Block) < (d.l/Block);
	else return c.r<d.r;
}
il void deal(int pos,int y)
{
	if(num[a[pos]]==a[pos]) summ--;
	else if((num[a[pos]]+y)==a[pos]) summ++;
	num[a[pos]]+=y;
}
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=n;i++) a[i]=read();
	for(re int i=1;i<=m;i++){
		Q[i].l=read(),Q[i].r=read(),Q[i].id=i;
	}
	Block=sqrt(n);
	sort(Q+1,Q+1+m,cmp);
	int ll=1,rr=0;
	for(re int i=1;i<=m;i++){//关系要注意 
		while(rr<Q[i].r) deal(++rr,1);
		while(ll>Q[i].l) deal(--ll,1);
		while(ll<Q[i].l) deal(ll++,-1);
		while(rr>Q[i].r) deal(rr--,-1);
		ans[Q[i].id]=summ;
	}
	for(re int i=1;i<=m;i++){
		printf("%d\n",ans[i]);
	}
	return 0;
}

100分:树状数组
首先将问题离线,按右端点排序
设num[i]表示数值为i的数字有多少个
然后右端点每次移动,都单点更新num,然后更新贡献,统计区间和就可以了
关键是:怎么统计贡献?
我们发现,对于每一个数字a[i],当num[a[i](算上自己)恰好有a[i]个时,会对答案有贡献
因为是按照右端点排序,所以将贡献记在左端点上,如果记在别的地方就会多统计贡献
因为当每一个数字最后一次出现的下标发生变化之后,贡献标记的位置发生了变化,所以
还要移动贡献标记,为此我们用vector存前面数值相同的数字的下标
然鹅,这样做不够……
e.g.
设数值为3的数字的下标:
5 8 17 66 81 92 97
如果66是最后出现的3的位置,那66的贡献记在17上面。
而当右端点位于[66,80]时,左端点在[6,8]则答案+1,在[1,5]
里则答案不变,所以作为补偿,在5处打上补偿标记-1。
同样地,补偿标记也要移动。
当左端点移动到[81,91]时,-1要记在8头上,1要记在17头上
此时,5处+1,8处-1,17处+1

单点修改,区间查询-----> 树状数组
对于每一种值,需要存一个链表(或者队列)来索引下标

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
il int read()
{
	int s=0,w=1;char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') w=-1;c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<1)+(s<<3)+c-'0';c=getchar();
	}
	return s*w;
} 
const int N=1e6+10;
int n,m,a[N],L[N],R[N],id[N],ans[N];
int TR[N];
vector<int> v[N];
il bool cmp(int x,int y){
	if(R[x]!=R[y]) return R[x]<R[y];
	else return L[x]<L[y];
}
il int lowbit(int x){ return x&(-x); }
il void upd(int x,int y)
{
	while(x<=n){
		TR[x]+=y;x+=lowbit(x);
	}return ;
}
il int getsum(int x){
	int res=0;
	while(x>0){
		res+=TR[x];
		x-=lowbit(x);
	}return res;
}
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=n;i++) a[i]=read();
	for(re int i=1;i<=m;i++){
		L[i]=read(),R[i]=read(),id[i]=i;
	}
	sort(id+1,id+1+m,cmp);
	int p=1;
	for(re int i=1;i<=n;i++){
		v[a[i]].push_back(i);
		int sz=v[a[i]].size();
		if(sz==a[i]) upd(v[a[i]][sz-a[i]],1);//打上贡献标记 
		else if(sz==a[i]+1){
			upd(v[a[i]][sz-a[i]-1] , -2);//打上补偿标记 
			upd(v[a[i]][sz-a[i]],1);//移动贡献标记 
		}
		else if(sz>a[i]+1){
			upd(v[a[i]][sz-a[i]-2],1);
			upd(v[a[i]][sz-a[i]-1],-2);//移动补偿标记 
			upd(v[a[i]][sz-a[i]],1);
		}
		while(p<=m && R[id[p]]==i){
			ans[id[p]]=getsum(R[id[p]])-getsum(L[id[p]]-1);
			p++;
		}
	}
	for(re int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

排序没用结构体,这样应该常数会小一些。

end

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-23 11:42:30  更:2021-09-23 11:43:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 16:11:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码