2018.7.21 绍兴一中模拟赛 解题报告

成绩: 100+60+50=210(考得一般,差不多刚好达到基础分)


T1:数学(math)

80分做法:

很简单,直接暴力枚举 1... m 1...m 1...m间的每一个数,计算其与 n n n g c d gcd gcd,并将结果相乘,即可得到答案。

这样的做法接近于 O ( n ) O(n) O(n)。因此只能得80分,并不能AC。

代码如下:

#include
#define YKH 1000000007
#define LL long long
using namespace std;
LL n,m,ans=1;
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;char ch;
	while(!isdigit(ch=tc()));
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline LL gcd(LL x,LL y)//求最大公因数
{
	return y?gcd(y,x%y):x;
}
int main()
{
	freopen("math.in","r",stdin),freopen("math_.out","w",stdout);
	register LL i;LL flag=2;
	read(n),read(m);
	for(i=1;i<=m;++i) (ans*=gcd(i,n))%=YKH;//暴力枚举
	return write(ans),0;
}
满分做法:

满分做法其实也并不难。

我们可以枚举n的每一个质因子(这只需要 O ( n ) O(\sqrt n) O(n )的时间),然后计算出在 1... m 1...m 1...m的范围中有多少个数含有这个质因子(可以直接用 m m m除以该质因子),然后快速幂求出这些因子的乘积即可。

我的代码中主要是用除余法来筛质因子,虽然这里用的并不标准,可能依然会有一部分的合数,但是在这题中并不影响答案,还能打打提高效率。

代码如下:

#include
#define YKH 1000000007
#define LL long long
using namespace std;
LL n,m,ans=1;
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;char ch;
	while(!isdigit(ch=tc()));
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
inline bool Is_Prime(LL x)//判断n是否已是质数,若是,则可直接计算出答案
{
	if(x<=1) return false;
	for(register LL i=2;i*i<=x;++i) if(!(x%i)) return false;
	return true;
}
inline LL quick_pow(LL x,LL y)//快速幂,大大减少计算乘积的时间
{
	LL res=1;
	while(y)
	{
		if(y&1) (res*=x)%=YKH;
		(x*=x)%=YKH,y>>=1;
	}
	return res;
}
inline void doing(LL val)
{
	LL x=val;//用一个变量来记录一下当前正在操作的数
	while(!(n%x)) (ans*=quick_pow(val,m/x))%=YKH,x*=val;//判断n中是否含有多个该因子,对于每一个该因子,用快速幂计算出其乘积
	n/=x/val;//将这个质因子从n中除去
	if(Is_Prime(n))//判断当前的n是否已是质数
	{
		write(ans*quick_pow(n,m/n)%YKH);//输出最终答案
		exit(0);//退出整个程序
	}
}
int main()
{
	freopen("math.in","r",stdin),freopen("math.out","w",stdout);
	register LL i;LL flag=2;//flag表示i每次增加的数,使i始终可以表示为6n±1的形式,且不遗漏
	read(n),read(m),doing(2),doing(3);//单独处理2,3两个质数
	if(Is_Prime(n)) return write(ans*quick_pow(n,m/n)%YKH),0;
	for(i=5;i<=m&&n>1;i+=flag,flag=6-flag) doing(i);//枚举每一个质数(6n±1不一定是质数,但大于2和3的质数一定可以用这种形式来表示)
	return write(ans),0;
}

T2:序列求和(magic)

60分做法:

直接 O ( n 2 ) O(n^2) O(n2)枚举序列中的每一个区间,并在枚举的同时不断更新该区间的最大值和异或值,从而求出每一段区间的答案,将其累加就是最终的答案。

代码如下:

#include
#define YKH 1000000007
#define LL long long
#define N 100000
using namespace std;
LL n,ans=0,a[N+5],s[N+5];
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;char ch;
	while(!isdigit(ch=tc()));
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int main()
{
	freopen("magic.in","r",stdin),freopen("magic.out","w",stdout);
	register LL i,j;
	for(read(n),i=1;i<=n;read(a[i]),s[i]=s[i-1]^a[i],++i);//用s[]数组累计异或值
	for(i=1;i<=n;++i)
	{
		LL MAX=0;//用一个变量来记录每个区间的最大值
		for(j=i;j<=n;++j)
			MAX=max(MAX,a[j]),(ans+=MAX*(s[j]^s[i-1]))%=YKH;//枚举区间,更新最大值,求出答案
	}
	return write(ans),0;
}
满分做法:

这题暂时还没有改满,等改满后再更新该部分的题解。


T3:DxxA(dxxa)

50分做法:

赤裸裸的暴力,代码如下:

#include
#define LL long long
#define N 500000
using namespace std;
LL n,A,B,C,ans=0,a[N+5],b[N+5],c[N+5];
inline char tc()
{
	static char ff[100000],*A=ff,*B=ff;
	return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
	x=0;char ch;
	while(!isdigit(ch=tc()));
	while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(LL x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int main()
{
	freopen("dxxa.in","r",stdin),freopen("dxxa.out","w",stdout);
	register LL i,j,k;
	for(read(n),read(A),read(B),read(C),i=1;i<=n;read(a[i]),read(b[i]),read(c[i++]));
	for(i=1;i<=A;++i)//暴力枚举A的大小
	{
		for(j=1;j<=B;++j)//暴力枚举B的大小
		{
			LL ok=1,Min=1;
			for(k=1;k<=n;++k)//枚举每一张卡牌,得出最小的C
			{
				LL flag=(i<=a[k]?0:1)+(j<=b[k]?0:1);//计算A和B中有几个不大于这张卡牌
				if(!flag||(flag==1&&c[k]==C))//如果无论如何都不可能有两个值大于当前卡牌的对应值,说明当前A和B的组合不合法
				{
					ok=0;
					break;
				}
				if(flag==1) Min=max(Min,c[k]+1);//否则更新C的最小值
			}
			if(ok) ans+=C-Min+1;//更新答案
		}	
	}
	return write(ans),0;
}
满分做法:

这题暂时还没有改满,等改满后再更新该部分的题解。

你可能感兴趣的:(比赛)