2018.10.12模拟赛

T1

题目:

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

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

solution:

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

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

但是因为要保证和在 [ l , r ] [l,r] [l,r]内不好处理,我们可以用总区间数 − - ( &lt; l 的 区 间 个 数 ) (&lt;l的区间个数) (<l) − - ( &gt; r 的 区 间 个 数 ) (&gt;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 n 个正整数序列 a 1 a1 a1, a 2 a2 a2,…, a n an an,每个序列长度为 m m m。 选择至少 1 1 1 个序列,在每个被选择的序列中选择一个元素,求出所有被选择的元素 的 g c d gcd gcd。 求所有方案的结果之和,答案对 1 e 9 + 7 1e9+7 1e9+7 取模。两种方案不同,当且仅当存在至少一 个元素,在一种方案中被选择,在另一种中没有。

n ≤ 20 n\le20 n20 m ≤ 100000 m\le100000 m100000

solution:

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

有了这个以后枚举每一个 g c d gcd 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 qwq

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值