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)
1 #include<cmath> 2 #include<ctime> 3 #include<queue> 4 #include<stack> 5 #include<cstdio> 6 #include<vector> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define N 100001 11 #define M 1000001 12 using namespace std; 13 struct building{ 14 int l,r,h; 15 }a[N]; 16 struct linetree{ 17 int l,r,h; 18 }lt[M]; 19 struct answer{ 20 int x,y; 21 }ans[N<<1]; 22 int p[N<<1],n,cnt,tot; 23 inline void build(int i,int l,int r){ 24 lt[i].l=l;lt[i].r=r; 25 if(l+1<r){ 26 int lef=i<<1,rig;rig=lef|1; 27 int mid=(l+r)>>1; 28 build(lef,l,mid); 29 build(rig,mid,r); 30 } 31 } 32 inline void cover(int i,int l,int r,int h){ 33 if(p[lt[i].l]>=l&&p[lt[i].r]<=r) 34 lt[i].h=max(lt[i].h,h); 35 else if(lt[i].l+1<lt[i].r){ 36 int lef=i<<1,rig;rig=lef|1; 37 int mid=(lt[i].l+lt[i].r)>>1; 38 lt[lef].h=max(lt[lef].h,lt[i].h); 39 lt[rig].h=max(lt[rig].h,lt[i].h); 40 if(r>p[mid]) cover(rig,l,r,h); 41 if(l<p[mid]) cover(lef,l,r,h); 42 } 43 } 44 inline void ask(int i){ 45 if(lt[i].l+1<lt[i].r){ 46 int lef=i<<1,rig;rig=lef|1; 47 lt[lef].h=max(lt[lef].h,lt[i].h); 48 lt[rig].h=max(lt[rig].h,lt[i].h); 49 ask(lef);ask(rig); 50 } 51 else{ 52 ans[++cnt].x=p[lt[i].l]; 53 ans[cnt].y=lt[i].h; 54 } 55 } 56 inline void init(){ 57 scanf("%d",&n); 58 for(int i=1;i<=n;i++){ 59 scanf("%d%d%d",&a[i].h,&a[i].l,&a[i].r); 60 p[++cnt]=a[i].l;p[++cnt]=a[i].r; 61 } 62 sort(p+1,p+1+cnt);tot=1; 63 for(int i=2;i<=cnt;i++) 64 if(p[i]!=p[i-1]) 65 p[++tot]=p[i]; 66 cnt=0; 67 build(1,1,tot); 68 for(int i=1;i<=n;i++) 69 cover(1,a[i].l,a[i].r,a[i].h); 70 cnt=0;ask(1); 71 ans[++cnt].x=p[tot]; 72 tot=0; 73 for(int i=1;i<=cnt;i++) 74 if(ans[i].y!=ans[i-1].y) 75 tot+=2; 76 printf("%d ",tot); 77 for(int i=1;i<=cnt;i++) 78 if(ans[i].y!=ans[i-1].y){ 79 printf("%d %d ",ans[i].x,ans[i-1].y); 80 printf("%d %d ",ans[i].x,ans[i].y); 81 } 82 } 83 int main(){ 84 freopen("build.in","r",stdin); 85 freopen("build.out","w",stdout); 86 init(); 87 fclose(stdin); 88 fclose(stdout); 89 return 0; 90 }
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;否则结束。
(综上所述就是一道傻逼题,然后但是好像是因为一个特别傻逼的错然后爆零???)
1 #include<cmath> 2 #include<ctime> 3 #include<queue> 4 #include<stack> 5 #include<cstdio> 6 #include<vector> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define L 12 11 #define Y 131 12 #define N 1001 13 #define M 100001 14 #define K 100003 15 using namespace std; 16 int h,t,sum; 17 int fir[K],nxt[K],cnt; 18 int u[N],to[M],la[N],lb[M],m,n,ans,tot; 19 char key[K][L],a[N][L],b[M][L]; 20 inline int hash(char a[],int l){ 21 int ret=0; 22 for(int i=1;i<=l;i++) 23 ret=(ret*Y*Y%K+a[i]*Y)%K; 24 return ret; 25 } 26 inline void init(){ 27 scanf("%d",&n); 28 for(int i=1;i<=n;i++){ 29 scanf("%s",a[i]+1); 30 la[i]=strlen(a[i]+1); 31 } 32 scanf("%d",&m); 33 for(int i=1;i<=m;i++){ 34 scanf("%s",b[i]+1); 35 lb[i]=strlen(b[i]+1); 36 } 37 for(int i=1,j,k;i<=n;i++){ 38 k=hash(a[i],la[i]); 39 for(j=fir[k];j;j=nxt[j]) 40 if(!strcmp(key[j]+1,a[i]+1)) 41 break; 42 if(!j){ 43 nxt[++cnt]=fir[k];fir[k]=cnt; 44 for(j=1;j<=la[i];j++) 45 key[cnt][j]=a[i][j]; 46 } 47 } 48 for(int i=1,j,k;i<=m;i++){ 49 k=hash(b[i],lb[i]); 50 for(j=fir[k];j;j=nxt[j]) 51 if(!strcmp(key[j]+1,b[i]+1)) 52 break; 53 if(j&&!u[j]) tot++; 54 u[j]++;to[i]=j; 55 } 56 memset(u,0,sizeof(u)); 57 ans=m;h=1; 58 for(t=1;t<=m;t++){ 59 if(to[t]&&!u[to[t]]) ++sum; 60 ++u[to[t]]; 61 while(sum==tot){ 62 ans=min(t-h+1,ans); 63 u[to[h]]--; 64 if(!u[to[h]]) sum--; 65 ++h; 66 } 67 } 68 if(!tot) ans=0; 69 printf("%d %d ",tot,ans); 70 } 71 int main(){ 72 freopen("word.in","r",stdin); 73 freopen("word.out","w",stdout); 74 init(); 75 fclose(stdin); 76 fclose(stdout); 77 return 0; 78 }
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了,只是都写炸了【我真是太弱了】)
1 #include<cmath> 2 #include<ctime> 3 #include<queue> 4 #include<stack> 5 #include<cstdio> 6 #include<vector> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define N 1005 11 using namespace std; 12 typedef long long ll; 13 int r[N][N],l[N][N],q[N],n,m,h,t,ans; 14 ll a[N][N],s[N][N],sum=1e15; 15 inline int read(){ 16 int ret=0;char c=getchar(); 17 while(!isdigit(c)) 18 c=getchar(); 19 while(isdigit(c)){ 20 ret=ret*10+c-'0'; 21 c=getchar(); 22 } 23 return ret; 24 } 25 inline ll read_ll(){ 26 ll ret=0;char c=getchar(); 27 while(!isdigit(c)) 28 c=getchar(); 29 while(isdigit(c)){ 30 ret=ret*10+c-'0'; 31 c=getchar(); 32 } 33 return ret; 34 } 35 inline void init(){ 36 n=read();m=read(); 37 for(int i=1;i<=n;i++) 38 for(int j=1;j<=m;j++) 39 a[i][j]=read_ll(); 40 for(int i=1;i<=n;i++) 41 for(int j=1;j<=m;j++) 42 s[i][j]=s[i][j-1]+a[i][j]; 43 for(int j=1;j<=m;j++) 44 for(int i=1;i<=n;i++) 45 s[i][j]+=s[i-1][j]; 46 for(int i=1,k;i<=n;i++){ 47 for(int j=1;j<=m;j=k){ 48 for(k=j;k<=m&&a[i][k];k++); 49 for(int l=j;l<k;l++) 50 r[i][l]=k; 51 while(k<=m&&!a[i][k]) ++k; 52 } 53 } 54 for(int j=1;j<=m;j++){ 55 h=1;t=0; 56 for(int i=1;i<=n;i++){ 57 while(h<=t&&r[i][j]<=r[q[t]][j]) t--; 58 l[i][j]=i-q[t];q[++t]=i; 59 } 60 } 61 q[0]=n+1; 62 for(int j=1;j<=m;j++){ 63 h=1;t=0; 64 for(int i=n;i;i--){ 65 while(h<=t&&r[i][j]<=r[q[t]][j]) t--; 66 l[i][j]+=q[t]-i-1;q[++t]=i; 67 ans=max(ans,min(l[i][j],r[i][j]-j)); 68 } 69 } 70 for(int j=1,k;j<=m;j++){ 71 for(int i=1;i<=n;i=k){ 72 for(k=i;k<=n&&r[k][j]-j>=ans;k++); 73 if(k-i>=ans){ 74 for(int l=k-ans;l>=i;l--) 75 sum=min(sum,s[l+ans-1][j+ans-1]-s[l-1][j+ans-1]-s[l+ans-1][j-1]+s[l-1][j-1]); 76 } 77 while(k<=n&&r[k][j]-j<ans) ++k; 78 } 79 } 80 printf("%d %lld ",ans*ans,sum); 81 } 82 int main(){ 83 freopen("matrix.in","r",stdin); 84 freopen("matrix.out","w",stdout); 85 init(); 86 fclose(stdin); 87 fclose(stdout); 88 return 0; 89 }