[日常训练]FJ省夏令营day1
T1
Description
给出n个矩形的顶点坐标(每个矩形的底边都在x轴上),求这n个矩形所组成图形的轮廓线的顶点。
Input
第一行一个整数n,表示矩形个数。
以下n行,每行3个整数,分别表示矩形的x坐标区间及矩形的高度h[i]。
Output
第一行一个整数m,表示轮廓线顶点个数。
以下m行,每行一个坐标表示轮廓线上的顶点。从左到右遍历轮廓线并顺序输出顶点。第一个和最后一个节点的y坐标必然为0。
Sample Input
2
3 0 2
4 1 3
Sample Output
6
0 0
0 3
1 3
1 4
3 4
3 0
HINT
1<=n<=100000,1<=h[i]<=109,-109<=l[i]<r[i]<=109
Solution
这道题是离散+线段树的裸题了。用扫描线+堆也是可以做的。(由于本弱太弱,所以线段树不小心写炸了QAQ)
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 100001
#define M 1000001
using namespace std;
struct building{
int l,r,h;
}a[N];
struct linetree{
int l,r,h;
}lt[M];
struct answer{
int x,y;
}ans[N<<];
int p[N<<],n,cnt,tot;
inline void build(int i,int l,int r){
lt[i].l=l;lt[i].r=r;
if(l+<r){
int lef=i<<,rig;rig=lef|;
int mid=(l+r)>>;
build(lef,l,mid);
build(rig,mid,r);
}
}
inline void cover(int i,int l,int r,int h){
if(p[lt[i].l]>=l&&p[lt[i].r]<=r)
lt[i].h=max(lt[i].h,h);
else if(lt[i].l+<lt[i].r){
int lef=i<<,rig;rig=lef|;
int mid=(lt[i].l+lt[i].r)>>;
lt[lef].h=max(lt[lef].h,lt[i].h);
lt[rig].h=max(lt[rig].h,lt[i].h);
if(r>p[mid]) cover(rig,l,r,h);
if(l<p[mid]) cover(lef,l,r,h);
}
}
inline void ask(int i){
if(lt[i].l+<lt[i].r){
int lef=i<<,rig;rig=lef|;
lt[lef].h=max(lt[lef].h,lt[i].h);
lt[rig].h=max(lt[rig].h,lt[i].h);
ask(lef);ask(rig);
}
else{
ans[++cnt].x=p[lt[i].l];
ans[cnt].y=lt[i].h;
}
}
inline void init(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d%d",&a[i].h,&a[i].l,&a[i].r);
p[++cnt]=a[i].l;p[++cnt]=a[i].r;
}
sort(p+,p++cnt);tot=;
for(int i=;i<=cnt;i++)
if(p[i]!=p[i-])
p[++tot]=p[i];
cnt=;
build(,,tot);
for(int i=;i<=n;i++)
cover(,a[i].l,a[i].r,a[i].h);
cnt=;ask();
ans[++cnt].x=p[tot];
tot=;
for(int i=;i<=cnt;i++)
if(ans[i].y!=ans[i-].y)
tot+=;
printf("%d\n",tot);
for(int i=;i<=cnt;i++)
if(ans[i].y!=ans[i-].y){
printf("%d %d\n",ans[i].x,ans[i-].y);
printf("%d %d\n",ans[i].x,ans[i].y);
}
}
int main(){
freopen("build.in","r",stdin);
freopen("build.out","w",stdout);
init();
fclose(stdin);
fclose(stdout);
return ;
}
build
T2
Description
分别给你n个单词(n<=103)作为单词表和一篇包含m个单词(m<=105)的文章。
在文章中求出一段连续的区间使得区间所包含的单词表中的单词数最多(同个单词出现多次算一个)且区间内单词数最少。
Input
第一行一个数n,表示单词表单词数。
接下来n行,每行是一个长度不超过10的字符串,表示一个单词表中的单词。
接着是一个数m,表示文章中的单词数。
然后是m行长度不超过10的字符串,每个表示文章中的一个单词。
Output
输出文件共2行。
第1行为文章中最多包含的单词表单词数。
第2行表示在文章中包含最多单词表中的单词的最短的连续段的长度。
Sample Input
3
hot
dog
milk
5
hot
dog
dog
milk
hot
Sample Output
3
3
Solution
这道题其实是由2个子问题组成:求区间所包含的单词表中的单词数(以下简称所含单词数)最大值ans和满足最大值的最短区间长度。
前者只需要扫一遍文章,哈希判断是否在单词表中即可。
后者由于数据范围不支持O(n2)的算法,所以需运用到双指针扫描:
1. 将头尾指针指向第一个元素;
2. 向右移动尾指针,直到指到最后一个元素或区间内所含单词数=ans;
3. 向右移动头指针,直到区间为空或再向右移一个会导致区间内所含单词数≠ans,最短区间长度len=min(len,当前区间长度);
4.如果尾指针没有指向最后一个元素,将头尾指针均向右移一个,返回步骤2;否则结束。
(综上所述就是一道傻逼题,然后但是好像是因为一个特别傻逼的错然后爆零???)
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define L 12
#define Y 131
#define N 1001
#define M 100001
#define K 100003
using namespace std;
int h,t,sum;
int fir[K],nxt[K],cnt;
int u[N],to[M],la[N],lb[M],m,n,ans,tot;
char key[K][L],a[N][L],b[M][L];
inline int hash(char a[],int l){
int ret=;
for(int i=;i<=l;i++)
ret=(ret*Y*Y%K+a[i]*Y)%K;
return ret;
}
inline void init(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",a[i]+);
la[i]=strlen(a[i]+);
}
scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%s",b[i]+);
lb[i]=strlen(b[i]+);
}
for(int i=,j,k;i<=n;i++){
k=hash(a[i],la[i]);
for(j=fir[k];j;j=nxt[j])
if(!strcmp(key[j]+,a[i]+))
break;
if(!j){
nxt[++cnt]=fir[k];fir[k]=cnt;
for(j=;j<=la[i];j++)
key[cnt][j]=a[i][j];
}
}
for(int i=,j,k;i<=m;i++){
k=hash(b[i],lb[i]);
for(j=fir[k];j;j=nxt[j])
if(!strcmp(key[j]+,b[i]+))
break;
if(j&&!u[j]) tot++;
u[j]++;to[i]=j;
}
memset(u,,sizeof(u));
ans=m;h=;
for(t=;t<=m;t++){
if(to[t]&&!u[to[t]]) ++sum;
++u[to[t]];
while(sum==tot){
ans=min(t-h+,ans);
u[to[h]]--;
if(!u[to[h]]) sum--;
++h;
}
}
if(!tot) ans=;
printf("%d\n%d\n",tot,ans);
}
int main(){
freopen("word.in","r",stdin);
freopen("word.out","w",stdout);
init();
fclose(stdin);
fclose(stdout);
return ;
}
word
T3
Description
给定一个N行M列(N,M<=1000)的非负整数矩阵,求一个最大的正方形子矩阵,该矩阵满足:
矩阵中每一个元素权值都大于0;
在满足上述条件的前提下,矩阵面积最大;
在满足上述条件的前提下,选择元素和最小的。
Input
第一行两个整数N,M。
接下来N行,每行M个整数。
Output
2个数,用空格隔开,第一个数为满足条件的矩阵的面积,第二个数为该矩阵各元素之和。
Sample Input
3 7
1 1 1 0 2 1 1
1 1 1 0 1 1 1
1 1 1 0 1 1 1
Sample Output
9 9
Solution
这道题可以分解成2个子问题:求满足条件的矩阵的最大边长len、满足最大边长的最小元素和sum,记输入的矩阵为a[][]。
前者可以通过枚举正方形的左边所在列j,然后求出以当前行i的最大长度为长,向上/下最多可以扩展到哪(即求第一个长度小于当前行的,单调队列即可),边长=min(长,宽)。
===============================================以下为具体步骤,不喜请跳过===============================================
前者可以先枚举正方形的左边所在列j,然后对于每一行i,求出从a[i][j]开始,向右连续的最右边的元素为a[i][k],记为rig[i][j]。
然后分别求出从a[i][j]开始,以rig[i][j]-j+1为长,向上和向下最多能扩展到哪,记为top[i][j]、down[i][j],
此时能形成的正方形边长为min(rig[i][j]-j+1,top[i][j]-down[i][j]+1),将这些值取最大值就是答案len了。
其中,rig[][]能通过O(n2)预处理处理出来,top[][]、down[][]可以分别用两个单调递增的单调队列求出来
(也就是求向上、向下第一个使得r[i][k]<r[i][j]的k,top[i][j]=k+1,down[i][j]=k-1),时间复杂度O(n2),枚举i,j也是O(n2)。
后者可以通过用类似上述方法判断当前i,j是否满足以(i,j)为左上角顶点的满足条件的正方形边长是否等于len。如果满足条件,计算出这个正方形的和s,则sum=min(sum,s)。
这个不必每次枚举计算,可以通过二维前缀和O(n2)预处理,O(1)求出。s=s[i+len-1][j+len-1]-s[i-1][j+len-1]-s[i+len-1][j-1]+s[i-1][j-1]。
(其实这天的题我口头AK了,只是都写炸了【我真是太弱了】)
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1005
using namespace std;
typedef long long ll;
int r[N][N],l[N][N],q[N],n,m,h,t,ans;
ll a[N][N],s[N][N],sum=1e15;
inline int read(){
int ret=;char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c)){
ret=ret*+c-'';
c=getchar();
}
return ret;
}
inline ll read_ll(){
ll ret=;char c=getchar();
while(!isdigit(c))
c=getchar();
while(isdigit(c)){
ret=ret*+c-'';
c=getchar();
}
return ret;
}
inline void init(){
n=read();m=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
a[i][j]=read_ll();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
s[i][j]=s[i][j-]+a[i][j];
for(int j=;j<=m;j++)
for(int i=;i<=n;i++)
s[i][j]+=s[i-][j];
for(int i=,k;i<=n;i++){
for(int j=;j<=m;j=k){
for(k=j;k<=m&&a[i][k];k++);
for(int l=j;l<k;l++)
r[i][l]=k;
while(k<=m&&!a[i][k]) ++k;
}
}
for(int j=;j<=m;j++){
h=;t=;
for(int i=;i<=n;i++){
while(h<=t&&r[i][j]<=r[q[t]][j]) t--;
l[i][j]=i-q[t];q[++t]=i;
}
}
q[]=n+;
for(int j=;j<=m;j++){
h=;t=;
for(int i=n;i;i--){
while(h<=t&&r[i][j]<=r[q[t]][j]) t--;
l[i][j]+=q[t]-i-;q[++t]=i;
ans=max(ans,min(l[i][j],r[i][j]-j));
}
}
for(int j=,k;j<=m;j++){
for(int i=;i<=n;i=k){
for(k=i;k<=n&&r[k][j]-j>=ans;k++);
if(k-i>=ans){
for(int l=k-ans;l>=i;l--)
sum=min(sum,s[l+ans-][j+ans-]-s[l-][j+ans-]-s[l+ans-][j-]+s[l-][j-]);
}
while(k<=n&&r[k][j]-j<ans) ++k;
}
}
printf("%d %lld\n",ans*ans,sum);
}
int main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
init();
fclose(stdin);
fclose(stdout);
return ;
}
matrix
[日常训练]FJ省夏令营day1的更多相关文章
- 「日常训练」ZgukistringZ(Codeforces Round #307 Div. 2 B)
题意与分析(CodeForces 551B) 这他妈哪里是日常训练,这是日常弟中弟. 题意是这样的,给出一个字符串A,再给出两个字符串B,C,求A中任意量字符交换后(不限制次数)能够得到的使B,C作为 ...
- 「日常训练」 Fire!(UVA-11624)
与其说是训练不如说是重温.重新写了Java版本的代码. import java.util.*; import java.math.*; import java.io.BufferedInputStre ...
- 「日常训练」COMMON 约数研究(HYSBZ-1968)
题意与分析 感谢https://www.cnblogs.com/Leohh/p/7512960.html的题解.这题话说原来不在我的训练范围,正好有个同学问我,我就拿来做做.数学果然不是我擅长的啊,这 ...
- 「日常训练」 Mike and Fun (CFR305D2B)
题意(CodeForces 548B) 每次对01矩阵中的一位取反,问每次操作后,单列中最长连续1的长度. 分析 非常非常简单,但是我当时训练的时候WA了四次...无力吐槽了,人间 不值得.jpg 代 ...
- 「日常训练」Common Subexpression Elimination(UVa-12219)
今天做的题目就是抱佛脚2333 懂的都懂. 这条题目干了好几天,最后还是参考别人的代码敲出来了,但是自己独立思考了两天多,还是有收获的. 思路分析 做这条题我是先按照之前的那条题目(The SetSt ...
- 集训队日常训练20181117 DIV2
大佬们一顿操作猛如虎,拼命AC强啊 4262: 区间异或 Time Limit(Common/Java):1000MS/3000MS Memory Limit:65536KByteTotal ...
- [日常训练]string
Description 给定一个长度为$n$的字符串,串中的字符保证是前$k$个小写字母.你可以在字符串后再添加$m$个字符,使得新字符串所包含的不同的子序列数量尽量多.当然,前提是只能添加前$k$个 ...
- [日常训练]yayamao的神题
Description $yayamao$是数学神犇,一天他在纸上计算起了$1/P$, 我们知道按照模拟除法可以得到准确解,例如$1/7=0.(142857),1/10=0.1(0)$.$yayama ...
- [日常训练]mod
Description 给定$p_1,p_2,-,p_n,b_1,b_2,...,b_m$, 求满足$x\;mod\;p_1\;\equiv\;a_1,x\;mod\;p_2\;\equiv\;a_2 ...
随机推荐
- GIT常用命令备忘
Git配置 git config --global user.name "storm" git config --global user.email "stormzhan ...
- expect的爱恨情仇
背景 openvpn生成证书想把它做成一键化,这样添加新用户时候就方便 遇到的问题 我的代码 gg_vpn_keys.exp #!/usr/bin/expect set user [lindex $a ...
- scala 学习笔记(02) 元组Tuple、数组Array、Map、文件读写、网页抓取示例
package yjmyzz import java.io.PrintWriter import java.util.Date import scala.io.Source object ScalaA ...
- 如何在mac本上安装android sdk
众所周知的原因,google的很多网站在国内无法访问,苦逼了一堆天朝程序员,下是在mac本上折腾android 开发环境的过程: 一.先下载android sdk for mac 给二个靠谱的网址: ...
- 跟我学习Storm_Storm主要特点
Storm拥有低延迟.高性能.分布式.可扩展.容错等特性,可以保证消息不丢失,消息处理严格有序.Storm的主要特点如下所示: 简单的编程模型.类似于MapReduce降低了并行批处理复杂性,Stor ...
- Jump Game 的三种思路 - leetcode 55. Jump Game
Jump Game 是一道有意思的题目.题意很简单,给你一个数组,数组的每个元素表示你能前进的最大步数,最开始时你在第一个元素所在的位置,之后你可以前进,问能不能到达最后一个元素位置. 比如: A = ...
- Javascript的精华啊【如果以后我看到了或者想到了再继续补吧】
我不过略有一些讨人喜欢的地方而已,怎么会有什么迷人的魔力呢? 一.语法 JS只有一个数字类型,64位浮点数,所以1和1.0是相同的.为什么这么设计:防止短整型的溢出. 二.对象 1.通常将一个对象的值 ...
- Google搜索的几个使用技巧——让你的搜索结果更准确
对于软件开发人员来说,不知道的内容在网上搜索是再正常不过的了.今天同事在组内分享了几个谷歌搜索的使用技巧,在此自己总结一下,希望可以帮到更多人. 在此之前先要唠叨几句,什么时候用百度,什么时候用谷歌? ...
- RabbitMQ集群环境搭建-4
确保成功安装好JDK,erlang,RabbitMQ等,并且RabbitMQ能正常启动,多台电脑之间能互相ping得通. 1. 安装 erlang.rabbitmq 如: 192.168.1.1.19 ...
- ZeroClipboard / jquery.zclip.min.js跨浏览器复制插件使用中遇到的问题解决
之前写过一个淘宝优惠券连接PC端转手机端连接的小工具,当时写到将转换好的url复制到剪切板这块时解决了IE和火狐,就是没办法搞定Chrome,知道可以通过flash搞定,但是觉得太麻烦没有仔细研究. ...