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的更多相关文章

  1. 「日常训练」ZgukistringZ(Codeforces Round #307 Div. 2 B)

    题意与分析(CodeForces 551B) 这他妈哪里是日常训练,这是日常弟中弟. 题意是这样的,给出一个字符串A,再给出两个字符串B,C,求A中任意量字符交换后(不限制次数)能够得到的使B,C作为 ...

  2. 「日常训练」 Fire!(UVA-11624)

    与其说是训练不如说是重温.重新写了Java版本的代码. import java.util.*; import java.math.*; import java.io.BufferedInputStre ...

  3. 「日常训练」COMMON 约数研究(HYSBZ-1968)

    题意与分析 感谢https://www.cnblogs.com/Leohh/p/7512960.html的题解.这题话说原来不在我的训练范围,正好有个同学问我,我就拿来做做.数学果然不是我擅长的啊,这 ...

  4. 「日常训练」 Mike and Fun (CFR305D2B)

    题意(CodeForces 548B) 每次对01矩阵中的一位取反,问每次操作后,单列中最长连续1的长度. 分析 非常非常简单,但是我当时训练的时候WA了四次...无力吐槽了,人间 不值得.jpg 代 ...

  5. 「日常训练」Common Subexpression Elimination(UVa-12219)

    今天做的题目就是抱佛脚2333 懂的都懂. 这条题目干了好几天,最后还是参考别人的代码敲出来了,但是自己独立思考了两天多,还是有收获的. 思路分析 做这条题我是先按照之前的那条题目(The SetSt ...

  6. 集训队日常训练20181117 DIV2

    大佬们一顿操作猛如虎,拼命AC强啊 4262: 区间异或  Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByteTotal ...

  7. [日常训练]string

    Description 给定一个长度为$n$的字符串,串中的字符保证是前$k$个小写字母.你可以在字符串后再添加$m$个字符,使得新字符串所包含的不同的子序列数量尽量多.当然,前提是只能添加前$k$个 ...

  8. [日常训练]yayamao的神题

    Description $yayamao$是数学神犇,一天他在纸上计算起了$1/P$, 我们知道按照模拟除法可以得到准确解,例如$1/7=0.(142857),1/10=0.1(0)$.$yayama ...

  9. [日常训练]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 ...

随机推荐

  1. PHP命令行模式

    <?php error_reporting(E_ALL); header('Content-Type:text/plain;charset=utf-8'); interface CommandA ...

  2. usb驱动开发22之驱动生命线

    我们总是很喜欢高潮,不是吗?那就好好对待她哦.我们来看一下linux中的高潮部分设备是怎么从Address进入Configured的. usb_set_configuration函数的代码就不贴了,可 ...

  3. Tree Traversals

    Tree Traversals 原题链接 常见的二叉树遍历的题目,根据后序遍历和中序遍历求层次遍历. 通过后序遍历和中序遍历建立起一棵二叉树,然后层序遍历一下,主要难点在于树的建立,通过中序遍历和后序 ...

  4. android:ToolBar详解(手把手教程)(转)

    来源 http://blog.mosil.biz/2014/10/android-toolbar/ 编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅 ...

  5. Incorrect string value异常解决

    mysql数据库的一个问题 1366-Incorrect string value:'\xE5\x8D\xA1\xE5......' for column 'filename' at row 1 问题 ...

  6. java并发:线程同步机制之ThreadLocal

    1.简述ThreadLocal ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程.ThreadLocal是一个线程级别的局部变量 ...

  7. [leetcode]算法题目 - Sudoku Solver

    最近,新加坡总理李显龙也写了一份代码公布出来,大致瞧了一眼,竟然是解数独题的代码!前几天刚刚写过,数独主要算法当然是使用回溯法.回溯法当时初学的时候在思路上比较拧,不容易写对.写了几个回溯法的算法之后 ...

  8. 学堂在线 UWP 首版

    好久没有写博客了,主要是最近在写一个小小的App.<( ̄︶ ̄)> 不知道看各位有木有爱看慕课的,作为一名资深的大三学渣的我有看慕课的习惯.一直在看学堂在线的慕课,感觉质量确实还可以,但是遗 ...

  9. 怎样关闭google的自动更新

    谷歌的自动更新很烦人的,只要你点击关于Google Chrome,谷歌就会自动更新成最新版本. 但是sencha框架好像与谷歌29.0以上的兼容性不是很好,所以关闭谷歌自动更新的需求来了,网上很多人说 ...

  10. 探究JVM——垃圾回收

    垃圾回收主要考虑三件事情:哪些内存需要回收?什么时候回收?如何回收? 一.哪些内存需要回收? 堆内存:对于JVM 来说,垃圾回收主要是针对堆内存中的对象实例. 方法区:垃圾收集行为在方法区是比较少出现 ...