今天由于一些细节错误丢了80分…,但整体来说考的不大好
第一题 丢番图
题目:
考虑如下的丢番图方程:1/x+1/y=1/n(x,y,n∈N*)
小G 对下面这个问题十分感兴趣:对于一个给定的正整数n ,有多少种本质不同的解满足方
程(1)?例如n=4,有三种本质不同(x≤y)的解:
1/5+1/20=1/4
1/6+1/12=1/4
1/8+1/8=1/4
考的时候发现是之前联赛组讲过的题,但忘记怎么证明了。
对原方程,我们可以变化一下:
1/x+1/y=1/n →(x+y)/xy=1/n→nx+ny=xy→nx/(x-n)=y
设k=x-n,x=k+n,
原式:
n(k+n)/k=y→
n2
/k+n=y
因为y为整数,所以k时
n2
的因数,则我们可以通过用
n2
的因数来凑出k,于是答案,就是将
n2
的质因数指数+1相乘,除以2(有重复),加1,有一个是x=y的。
贴代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int sum,m,i;
long long n,ans;;
int main(){
scanf("%lld",&n);
m=sqrt(n),i=2,ans=1;
while (i<=n&&i<=m){
if (!(n%i)){
sum=0;
while (!(n%i))sum++,n/=i;
ans*=sum+sum+1;
}
i++;
}
if (n!=1)ans*=3;
printf("%lld\n",(ans+1)/2);
return 0;
}
第二题 广告计划
题目:
如今,在建筑的墙面上或者篱笆桩的表面上涂上一些广告,是一种新的吸引眼球的方法。
现在,小G 运营的一家小公司,决定也试着这样做做广告。小G 在他的篱笆桩上腾出了一些地方供广告使用。每一个篱笆桩都是一个水平的1*1*L 的4 棱柱,其中有一个1*L 的面是可以做广告的。1*L 的面上划出了L 个1*1 的小正方形(更具体地说是连续L 个水平排列的正方形),每个正方形内写上一个字母。
时间久了,广告做多了难免会出现一些比较麻烦的情况,比如计划改变或者制作出错,因此小G 的仓库里面积累了好多没有用的,上面已经写上L 个字母的篱笆桩。(所有的篱笆桩的大小都是一样的,他们唯一的区别仅仅在于上面写了什么字母)。
小G 决定对于这些篱笆桩进行重新利用,并且有了一些新的想法。
如果将这些篱笆桩竖直的叠放起来,并且依次从左往右,对于每一个篱笆桩顺次从上到下读出上面的字母,那么我们可以得到一些新的比较长的单词,如下图:
这些新的单词能满足小G 的一些新的需要。当然,基于美学考虑,小G 是不允许你删改篱笆桩上已经写上的字母的。
我们更具体地描述这个过程。我们将K 个长度为L 的篱笆桩叠在一起,可以得到一个写有K行L 列共K*L 个字母的面,每一个字母都在对应的唯一的格子里。我们从左上角开始依次向下读出每一个字母可以得到一个字母的序列,比如上图中的这个例子,那么我们读出的结果就是“TOEIIZENITKN”。如果,这个串中有我们所需要的单词,那么显然我们只需要将一些格子刷白,就可以得到我们所需要的了。举个例子,比如小G 想要给圣彼得堡的足球队泽尼特队做个广告,那么很显然只要按照上图中的做法就可以达到小G 想要的效果了。
现在小G已经想好了要做怎样的广告,同时也提供给你了小G仓库中的篱笆桩的类型的描述,你可以认为每一种类型的篱笆桩都是有无数个的。现在小G 想知道至少需要多少个篱笆桩叠起来才可以做出小G 想要的广告。
这题由于我是看错题了…于是爆零了….
注意!!留下的字母是连续的一片,中间没有空白。。。
我们可以发现,其实我们不必像题目里所说的那样竖着来匹配(注意这点很重要),假设已知答案k,我们要判断它是否合法,那么我们只需枚举起始点从哪里开始(我们可以将最后的广告视作一个k*m的坐标系),我们就可以得出最终不涂白的地方是什么样子的,然后暴力横着(在目标串中是每隔k一个字符)(可以hash或trie)匹配,就可以了。
通过枚举答案,再检测,时间复杂度是o(
n4
)(如果有人算到o(
n5
)请仔细想想 提示:匹配那里,对目标串是跳着匹配的)
贴代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define N 201
int n,m,L,ans;
int d[N];
char a[N][N+N],s[N+N];
void init(){
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
scanf(" %s",&a[i]);
scanf(" %s",&s);
}
bool did(int l,int len,int x,int bz){
static char c[N];
static int sum;
static bool p;
sum=0;
for (;l<L;c[sum++]=s[l],l+=len);
for (int i=1;i<=n;i++){
p=1;
for (int j=0;j<sum&&p;j++)
if (c[j]!=a[i][j+x])p=0;
d[bz]=i;
if (p)return 1;
}
return 0;
}
bool jian(int len,int x,int y){
for (int i=x;i<=len;i++)
if (!did(i-x,len,y,i))return 0;
for (int i=1;i<x;i++)
if (!did(i+len-x,len,y+1,i))return 0;
return 1;
}
void work(){
L=strlen(s);
for (int i=1;i<=L;i++)
for (int j=1;j<=i;j++)
for (int k=0;k<m;k++)
if (jian(i,j,k)){
ans=i;
return;
}
}
void write(){
if (!ans)printf("-1");
else{
printf("%d\n",ans);
for (int i=1;i<=ans;i++)
printf("%d ",d[i]);
}
}
int main(){
init();
work();
write();
return 0;
}
第三题 美丽家园
题目
现在富裕的家庭都有了自己的别墅,别墅的院子往往大家选择铺上黑白两色的地砖。如何铺地砖是很有学问的,因为这涉及到美学方面的问题。怎样铺是美的呢?先看下面两组:
Figure 1中的就被人们认定为比较美丽的,而Figure 2 中的铺法则是不美丽的了。因为人们认为,如果出现一块2*2的区域里,只有一种颜色,那这就是不美丽的,反之则是美丽的。
现在,小G的公司拿到了一个任务,一块N*M的院子必须铺上地砖。爱美之心人皆有之,主人家自然希望这块院子地砖的铺法是美丽的,所以他首先想让小G的公司拿出设计方案的总数来。而且这户主人家似乎对P这个数特别迷信,所以他希望得到的总方案数对P取模后的结果。
这个一看到n特别大,m特别小就感觉要用矩阵快速幂了。。
结果由于将两个功能不同的数组写到一块,我就挂了
对于这些图,我们可以视作01矩阵,按列转移,转移的话,我们可以暴力做出来初始转移矩阵,对于初始矩阵,我们全部设为1(先走1步),将n减1后,进行快速幂(n特别大,要用高精度)
贴代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 32
int m,k,p;
char s[201];
int b[N][N],c[N][N],a[N],help[9],n[N];
void init(){
scanf(" %s",&s);
scanf("%d %d",&m,&p);
}
bool jian(int x,int y){
static int xx,yy,xxx,yyy;
for (int i=1;i<m;i++){
xx=((x&help[i-1])>0);
xxx=((x&help[i])>0);
yy=((y&help[i-1])>0);
yyy=((y&help[i])>0);
if (xx==xxx&&xx==yy&&xxx==yyy)return 0;
}
return 1;
}
void pre(){
static int len,j;
len=strlen(s);
help[0]=1;
for (int i=1;i<=8;i++)
help[i]=help[i-1]*10;
for (int i=0;i<len;i++)
n[i/8+1]+=(s[len-i-1]-'0')*help[i%8];
k=(1<<m)-1;
for (int i=1;i<=8;i++)
help[i]=help[i-1]*2;
n[0]=(len-1)/8+1;
n[1]--;
j=1;
while (n[j]<0)n[j]+=100000000,n[++j]--;
while (!n[n[0]]&&n[0])n[0]--;
for (int i=0;i<=k;i++)
for (int j=i;j<=k;j++)
if (jian(i,j))b[i][j]=b[j][i]=1;
for (int i=0;i<=k;i++)
a[i]=1;
}
void div2(){
for (int i=n[0];i>=2;i--)
n[i-1]+=(n[i]&1)*100000000,n[i]/=2;
n[1]/=2;
while (!n[n[0]]&&n[0])n[0]--;
}
void work(){
while (n[0]){
if (n[1]&1){
for (int i=0;i<=k;i++)
for (int j=0;j<=k;j++)
c[0][j]=(c[0][j]+a[i]*b[i][j])%p;
for (int i=0;i<=k;i++)
a[i]=c[0][i],c[0][i]=0;
}
for (int i=0;i<=k;i++)
for (int j=0;j<=k;j++)
for (int l=0;l<=k;l++)
c[i][l]=(c[i][l]+b[i][j]*b[j][l])%p;
for (int i=0;i<=k;i++)
for (int j=0;j<=k;j++)
b[i][j]=c[i][j],c[i][j]=0;
div2();
}
}
void write(){
static int ans;
ans=0;
for (int i=0;i<=k;i++)
ans=(ans+a[i])%p;
printf("%d",ans);
}
int main(){
init();
pre();
work();
write();
return 0;
}