2018.10.30模拟赛

T1 YY

YY 有一个大矩阵 ( N ∗ M ) (N*M) NM, 矩阵的每个格子里都有一个整数权值 W [ i , j ] W[i,j] W[i,j]
( 1 &lt; = i &lt; = M , 1 &lt; = j &lt; = N ) (1&lt;=i&lt;=M,1&lt;=j&lt;=N) 1<=i<=M,1<=j<=N
对于这个矩阵 Y Y YY YY会有 P P P次询问,每次询问这个大矩阵的一个子矩阵内的最大值。

solution:

据说是二维 S T ST ST表裸题,然而我根本就不会 S T ST ST

于是学习了一下,顺便打了一下一维的模板

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
using namespace std;
int n,m,a[maxn],f[maxn][18];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline void prework(){
	for(int i=1;i<=n;i++) f[i][0]=a[i];
	int t=log2(n);
	for(int j=1;j<=t;j++)
		for(int i=1;i<=n-(1<<j)+1;i++)
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

inline int query(int l,int r){
	int k=log2(r-l+1);
	return max(f[l][k],f[r-(1<<k)+1][k]);
}

int main(){
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	prework();
	while(m--){
		int x=rd(),y=rd();
		printf("%d\n",query(x,y));
	}
	return 0;
}

然后是这道二维的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 305
using namespace std;
int n,m,q,a[maxn][maxn],f[maxn][maxn][10][10];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int max(int x,int y){return x>y?x:y;}

inline void prework(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			f[i][j][0][0]=a[i][j];
	int t1=log2(n)+1,t2=log2(m)+1;
	for(int i=0;i<=t1;i++)
		for(int j=0;j<=t2;j++){
			if(!i && !j) continue;
			for(int x=1;x<=n-(1<<i)+1;x++)
				for(int y=1;y<=m-(1<<j)+1;y++)
					if(!i) f[x][y][i][j]=max(f[x][y][i][j-1],f[x][y+(1<<(j-1))][i][j-1]);
					else f[x][y][i][j]=max(f[x][y][i-1][j],f[x+(1<<(i-1))][y][i-1][j]);
		}
}

inline int query(int x1,int y1,int x2,int y2){
	int kx=log2(x2-x1+1),ky=log2(y2-y1+1);
	int ans=f[x1][y1][kx][ky];
	ans=max(ans,f[x1][y2-(1<<ky)+1][kx][ky]);
	ans=max(ans,f[x2-(1<<kx)+1][y1][kx][ky]);
	ans=max(ans,f[x2-(1<<kx)+1][y2-(1<<ky)+1][kx][ky]);
	return ans;
}

int main(){
	freopen("yy.in","r",stdin);
	freopen("yy.out","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=rd();
	prework(); q=rd();
	while(q--){
		int x1=rd(),y1=rd(),x2=rd(),y2=rd();
		printf("%d\n",query(x1,y1,x2,y2));
	}
	return 0;
}

看起来也不那么难写 e m m m emmm emmm想不想知道我是怎么做的?

单调栈预处理 + + +分块!复杂度 O ( n 3 + s q r t ( n ) ∗ q ) O(n^3+sqrt(n)*q) O(n3+sqrt(n)q)好像跑的比正解快?

但是很卡空间,我卡到 220 + 220+ 220+差不多,惊险过

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 301
#define inf 0x3f3f3f3f
using namespace std;
int n,m,Q,q[maxn],a[maxn][maxn],f[maxn][maxn][maxn],bel[maxn],ans;
int blo,tot,g[19][19][maxn][maxn],L[19],R[19];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}

inline void prework(){
	int l,r,mx,i,j,now;
	for(i=1;i<=n;i++){//第i行
		for(j=1;j<=m;j++){//左端点 
			now=j+1; q[1]=a[i][j]; f[i][j][j]=a[i][j]; l=r=1;
			while(now<=m){
				while(l<=r && q[r]<a[i][now]) r--;
				q[++r]=a[i][now];
				f[i][j][now]=q[l]; now++;
			}
		}
	}
	blo=sqrt(n); tot=n/blo;
	for(i=1;i<=tot;i++){
		L[i]=(i-1)*blo+1; R[i]=i*blo;
		for(j=L[i];j<=R[i];j++) bel[j]=i;
	}
	if(R[tot]<n) tot++,L[tot]=R[tot-1]+1,R[tot]=n;
	for(i=L[tot];i<=R[tot];i++) bel[i]=tot;
	for(i=1;i<=m;i++)
		for(j=i;j<=m;j++){
			for(l=1;l<=tot;l++){
				mx=0;
				for(r=L[l];r<=R[l];r++)
					mx=max(mx,f[r][i][j]);
				g[l][l][i][j]=mx;
			}
			for(l=1;l<=tot;l++)
				for(r=l+1;r<=tot;r++)
					g[l][r][i][j]=max(g[l][r-1][i][j],g[r][r][i][j]);
		}
}

int main(){
	freopen("yy.in","r",stdin);
	freopen("yy.out","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=rd();
	prework();
	Q=rd();
	while(Q--){
		int x1=rd(),y1=rd(),x2=rd(),y2=rd();
		int l=bel[x1],r=bel[x2]; ans=0;
		if(l==r){
			for(int i=x1;i<=x2;i++)
				ans=max(ans,f[i][y1][y2]);
			printf("%d\n",ans); continue;
		}
		if(l+1<=r-1) ans=max(ans,g[l+1][r-1][y1][y2]);
		for(int i=x1;i<=R[l];i++)
			ans=max(ans,f[i][y1][y2]);
		for(int i=L[r];i<=x2;i++)
			ans=max(ans,f[i][y1][y2]);
		printf("%d\n",ans);
	}
	return 0;
}

T2 WYT

W Y T WYT WYT 有一把巨大的刷子,刷子的宽度为 M M M 米,现在 W Y T WYT WYT 要使用这把大刷
子去粉刷有 N N N 列的栅栏(每列宽度都为 1 1 1 米;每列的高度单位也为米,由输入
数据给出) 。
使用刷子的规则是:
1、 与地面垂直,从栅栏的底部向上刷
2、 每次刷的宽度为 M M M 米(当剩余栅栏宽度不够 M M M 米的话,刷子也可以使用,
具体看样例 2 2 2
3、 对于连续的 M M M 列栅栏,刷子从底向上,刷到的高度只能到这 M M M 列栅栏的最
低高度。
W Y T WYT WYT 请你回答两个问题:
1、最少有多少个单位面积不能刷到(单位面积为 1 1 1 平米)
2、在满足第一问的条件下,最少刷几次?

solution:

看到这道题莫名想到 s j z sjz sjz一小伙伴

同样的也打的是暴力···正解就是单调栈,和以前讲过的最大长方形很相似,但考场上第一反应是线段树

于是就平添 l o g log log

顺便吐槽一波 c m a t h cmath cmath库的 m a x   m i n max\ min max min函数真的慢啊,我 m a x max max没手写 T T T 70 70 70,加上就过了血亏

单调栈不带了写了放上考场代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 1000005
#define LL long long
#define ls cur<<1
#define rs cur<<1|1
#define inf 0x3f3f3f3f
using namespace std;
int n,m,a[maxn],mn[maxn<<2],l[maxn<<2],r[maxn<<2],lazy[maxn<<2],val[maxn];
LL ans1,ans2;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}

inline void pushup(int cur){
	mn[cur]=min(mn[ls],mn[rs]);
}
inline void pushdown(int cur){
	lazy[ls]=max(lazy[ls],lazy[cur]);
	lazy[rs]=max(lazy[rs],lazy[cur]);
}

inline void build(int cur,int L,int R){
	if(L==R){
		l[cur]=r[cur]=L; mn[cur]=a[L]; return;
	}
	int mid=(L+R)>>1;
	build(ls,L,mid); build(rs,mid+1,R);
	l[cur]=l[ls],r[cur]=r[rs]; pushup(cur);
}

inline void update(int cur,int L,int R,int c){
	if(L<=l[cur] && r[cur]<=R){
		lazy[cur]=max(lazy[cur],c); return;
	}
	pushdown(cur);
	int mid=(l[cur]+r[cur])>>1;
	if(L<=mid) update(ls,L,R,c);
	if(mid<R) update(rs,L,R,c);
	pushup(cur);
}

inline int query(int cur,int L,int R){
	if(L<=l[cur] && r[cur]<=R) return mn[cur];
	int mid=(l[cur]+r[cur])>>1,res=inf;
	if(L<=mid) res=min(res,query(ls,L,R));
	if(mid<R) res=min(res,query(rs,L,R));
	return res;
}

inline void update1(int cur){
	if(l[cur]==r[cur]) {
		val[l[cur]]=lazy[cur]; ans1+=a[l[cur]]-val[l[cur]];
		return;
	}
	pushdown(cur);
	update1(ls); update1(rs);
	return;
} 

int main(){
	freopen("wyt.in","r",stdin);
	freopen("wyt.out","w",stdout);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	build(1,1,n); int minn;
	for(int i=1;i<=n-m+1;i++){
		minn=query(1,i,i+m-1);
		update(1,i,i+m-1,minn);
	}
	update1(1);
	printf("%lld\n",ans1);
	for(int i=1;i<=n;){
		int now=i,cnt=0;
		while(now<=n && val[now]==val[i] && cnt<m){
			cnt++; now++;
		}
		i=now; ans2++;
	}
	printf("%lld\n",ans2);
	return 0;
}

T3 GG

2017 共有 N N N 棵树从 0 0 0 N − 1 N-1 N1 标号。现要把这些树种在一条直线上,第 i i i
树的种植位置 X[i]如下确定:
X [ 0 ] = X [ 0 ] % L X[0] = X[0] \%L X[0]=X[0]%L
X [ i ] = ( X [ i − 1 ] ∗ A + B ) % L X[i] = (X[i-1]*A+B) \% L X[i]=(X[i1]A+B)%L
每棵树种植的费用,是所有标号比它小的树与它的距离之和。2017 请你计
算各棵树的费用之积,最后对 1000000007 1000000007 1000000007 取余。

solution:

按照编号加,然后每次看他前面有多少后面有多少,把绝对值拆开算一下就好了,这可以用树状数组维护

考场上想到了但时间紧没调出来交了暴力后来发现正解还是比暴力分高???(或许石帅说的信仰有点用

最后发现正解写的有问题是因为 s b sb sb错误···血亏血亏

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<set>
#define maxn 200005
#define int long long
using namespace std;
int n,L,A,B,pos[maxn],mk[maxn],f[maxn],g[maxn],ans=1;
const int mod=1e9+7;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline void add(int x,int y){
	for(;x<=n;x+=x&-x) (f[x]+=y)%=mod,g[x]++;
}
inline int query(int x){
	int sum=0;
	for(;x;x-=x&-x) (sum+=f[x])%=mod; return sum;
}
inline int query1(int x){
	int sum=0;
	for(;x;x-=x&-x) sum+=g[x]; return sum;
}

struct quq{
	int id,val;
	bool operator <(const quq &x) const{
		return val<x.val;
	}
}a[maxn];
inline bool cmp(quq x,quq y){return x.id<y.id;}

inline int find(int x){
	int l=1,r=n,mid,res=0;
	while(l<=r){
		mid=(l+r)>>1;
		if(query1(mid)>=x) r=mid-1,res=mid;
		else l=mid+1;
	}
	return res;
}

signed main(){
	freopen("gg.in","r",stdin);
	freopen("gg.out","w",stdout);
	n=rd(); L=rd(); a[1].val=rd()%L; A=rd();B=rd(); a[1].id=1;
	for(int i=2;i<=n;i++){
		a[i].val=(a[i-1].val*A+B)%L; a[i].id=i;
	}
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++) pos[a[i].id]=i;
	sort(a+1,a+n+1,cmp); add(pos[1],a[1].val);
	for(int i=2;i<=n;i++){
		int x=query1(pos[i]); int qwq=query(pos[i]);
		int tmp=((x*a[i].val%mod-qwq)+mod)%mod;
		int tmp1=((query(n)-qwq+mod)%mod-(i-1-x)*a[i].val%mod+mod)%mod;
		ans=ans*((tmp+tmp1)%mod)%mod;
		add(pos[i],a[i].val);
	}
	printf("%lld\n",ans);
	return 0;
}

总分 100 + 70 + 10 = 180 100+70+10=180 100+70+10=180好气啊又掉 r a t i n g rating rating

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值