2018.10.12模拟赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/83043550

T1

题目:

给定一个 n m n*m 01 01 矩阵,求包含 [ l , r ] [l,r] 1 1 的子矩形个数。

n n \le 30 30 m m\le 50000 50000

solution:

考场上 A A 掉啦 q u q quq ,挺简单的,首先题目可以转化为找一个子矩阵让它的元素和在 [ l , r ] [l,r] 内,求子矩阵的个数 . .

看到 m m 特别大但是 n n 特别小,所以可以想到先预处理前缀和然后 n 2 n^2 枚举矩阵的上下两条边,然后因为求了前缀和,这个矩阵就可以抽象成一个数列,用双指针扫就好了 . .

但是因为要保证和在 [ l , r ] [l,r] 内不好处理,我们可以用总区间数 - ( < l ) (<l的区间个数) - ( > r ) (>r的区间个数)

就很好求了,注意边界问题,细节很多

放上考场代码有点丑

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 35
#define M 50005
#define LL long long
using namespace std;
int n,m,a[N][M],l,r,sum[M];
LL ans,f[M][N];
char s[M];

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 get_f(){
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			f[i][j]=f[i][j-1]+a[j][i];
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			f[i][j]+=f[i-1][j];
}

int main(){
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=rd(); m=rd();
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
			a[i][j]=s[j]-'0';
	}
	l=rd(); r=rd(); get_f();
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++){
			int ll=1,rr=1; LL res=0;
			for(int k=1;k<=m;k++)
				sum[k]=f[k][j]-f[k][i-1];
			for(int k=1;k<=m;k++)
			while(ll<=rr && ll<=m && rr<=m){//求小于l的 
				while(rr<=m && sum[rr]-sum[ll-1]<l) rr++;
				res+=rr-ll; ll++; if(rr>m) rr--;
				if(ll>rr) rr++;
			}
			ll=1,rr=1;
			while(ll<=rr && ll<=m && rr<=m){//求大于r的 
				while(rr<m && sum[rr]-sum[ll-1]<=r) rr++;
				if(rr==m && sum[rr]-sum[ll-1]<=r) {ll++;continue;}
				res+=m-rr+1; ll++;
				if(ll>rr) rr++;
			}
			ans+=1LL*m*(m+1)/2-res;
		}
	printf("%lld\n",ans);
	return 0; 
}

T2

一道看起来一脸不可做其实很简单的题

题目:

给定 n n 个正整数序列 a 1 a1 , a 2 a2 ,…, a n an ,每个序列长度为 m m 。 选择至少 1 1 个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素 的 g c d gcd 。 求所有方案的结果之和,答案对 1 e 9 + 7 1e9+7 取模。两种方案不同,当且仅当存在至少一 个元素,在一种方案中被选择,在另一种中没有。

n 20 n\le20 m 100000 m\le100000

solution:

考虑每个 g c d gcd 的贡献,设 f [ i ] [ j ] f[i][j] 表示 a i ai j j 的倍数的个数,这个可以开一个桶然后用类似埃式筛的方法求出来

有了这个以后枚举每一个 g c d gcd 运用乘法原理求出来方案数,但这样会有重复,所以就要用到容斥的思想

可以倒序枚举,然后每次用这一次算出来的减去后面是它的倍数的

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define N 25
#define M 100005
#define LL long long
using namespace std;
int n,m,a[N][M],f[N][M],backet[M],mx,maxx;
LL ans,g[N];
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;
}

int main(){
	freopen("b.in","r",stdin);
	freopen("b.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(),maxx=max(maxx,a[i][j]);
	for(int i=1;i<=n;i++){
		memset(backet,0,sizeof backet); mx=0;
		for(int j=1;j<=m;j++)
			backet[a[i][j]]++,mx=max(mx,a[i][j]);
		for(int j=1;j<=mx;j++){
			for(int k=j;k<=mx;k+=j)
				f[i][j]+=backet[k];
		}
	}
	for(int i=maxx;i;i--){
		g[i]=1;
		for(int j=1;j<=n;j++)
			g[i]=g[i]*(f[j][i]+1)%mod;
		g[i]--;
		for(int j=i+i;j<=maxx;j+=i)
			g[i]=(g[i]-g[j]+mod)%mod;
		(ans+=g[i]*i%mod)%=mod;
	}
	printf("%lld\n",ans);
	return 0;
}

只给了两个小时所以只让做两个题 q w q qwq

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/83043550