您的位置:首页 > 其它

sc2017新高二&高一模拟赛6 总结

2017-08-20 19:07 375 查看

T1:小J的五子棋(SMOJ2060)

题目分析:这题就是个很水的硬模拟。当一个棋子加进来的时候,我们用Hash看一下它的八个方向是否有能连成一条线的五个棋子即可。(听说直接开4∗108的bool数组也不会超空间,不过还是用Hash稳一点)

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=200010;
const int maxm=3005009;

const int M1=1333333331;
const int M2=1000000007;

const int u[4]={0,1,1,1};
const int v[4]={1,1,0,-1};

typedef long long LL;

struct data
{
int X,Y;
} Hash[maxm];

int n,m;

void Insert(int x,int y)
{
LL z=(long long)x*(long long)M1;
z%=(long long)maxm;
z+=( (long long)y*(long long)M2 );
z=(z%maxm)+maxm;
if (z>=maxm) z-=maxm;
while ( Hash[z].X<maxn )
{
z++;
if (z==maxm) z=0;
}
Hash[z].X=x;
Hash[z].Y=y;
}

bool Check(int x,int y)
{
LL z=(long long)x*(long long)M1;
z%=(long long)maxm;
z+=( (long long)y*(long long)M2 );
z=(z%maxm)+maxm;
if (z>=maxm) z-=maxm;
while ( Hash[z].X<maxn && ( Hash[z].X!=x || Hash[z].Y!=y ) )
{
z++;
if (z==maxm) z-=maxm;
}
return (Hash[z].X==x);
}

int main()
{
freopen("2060.in","r",stdin);
freopen("2060.out","w",stdout);

scanf("%d%d",&n,&m);
for (int i=0; i<maxm; i++) Hash[i].X=maxn;
for (int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
Insert(x,y);
bool f=false;
for (int j=0; j<4; j++)
for (int st=-4; st<=0; st++)
{
bool g=true;
for (int k=0; k<=4; k++)
g&=Check( x+(st+k)*u[j] , y+(st+k)*v[j] );
f|=g;
}
if (f)
{
printf("%d\n",i);
return 0;
}
}
printf("-1\n");

return 0;
}


T2:小J爱gcd(SMOJ2061)

题目分析:这题有两种做法,一种是分解质因数的方法。我们先将a与c分解,得到它们的每一个质因数及对应的幂,将幂分别乘以b或d。再将它们的质因数从小到大排序,扫一遍做快速幂即可得到答案,时间复杂度O(n−√log(n))。

还有一种时间复杂度为O(log2(n))的做法。假设b<d,我们不妨先求出(a,c)=x,然后将答案先乘上xb。若x=1,则直接输出答案,因为这说明a与c没有相同的质因数,即(ab,cd)也是1。否则(ax)b与(cx)bcd−b还可能对答案有贡献,但由于(ax)b与(cx)b的gcd已经为1,于是递归计算(ax)b与cd−b即可。由于每一次递归都至少使a或c中的一个数除以2,所以只会递归log(n)层。

CODE(分解质因数):

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=1000000;
const long long M=1000000007;
typedef long long LL;

struct data
{
int val;
LL Time;
} ;
data p[maxn];
data q[maxn];
int pcur=0,qcur=0;

int a,b,c,d;

bool Comp(data x,data y)
{
return x.val<y.val;
}

LL Fast_power(LL x,LL y)
{
if (!y) return 1LL;
LL mid=Fast_power(x,y>>1);
mid=mid*mid%M;
if (y&1) mid=mid*x%M;
return mid;
}

int main()
{
freopen("2061.in","r",stdin);
freopen("2061.out","w",stdout);

scanf("%d%d%d%d",&a,&b,&c,&d);
if ( (!b) || (!d) )
{
printf("1\n");
return 0;
}
if (a) swap(a,c),swap(b,d);
if (!a)
{
int ans=Fast_power(c,d);
printf("%d\n",ans);
return 0;
}

int sa=(int)floor( sqrt( (double)a )+1.0 );
for (int i=2; i<=sa; i++)
if ((a%i)==0)
{
p[++pcur].val=i;
while ((a%i)==0) a/=i,p[pcur].Time++;
p[pcur].Time*=(long long)b;
}
if (a!=1)
{
p[++pcur].val=a;
p[pcur].Time=b;
}
int sc=(int)floor( sqrt( (double)c )+1.0 );
for (int i=2; i<=sc; i++)
if ((c%i)==0)
{
q[++qcur].val=i;
while ((c%i)==0) c/=i,q[qcur].Time++;
q[qcur].Time*=(long long)d;
}
if (c!=1)
{
q[++qcur].val=c;
q[qcur].Time=d;
}

sort(p+1,p+pcur+1,Comp);
sort(q+1,q+qcur+1,Comp);
q[++qcur].val=M;

LL ans=1,tail=1;
for (int i=1; i<=pcur; i++)
{
while (q[tail].val<p[i].val) tail++;
if (p[i].val==q[tail].val)
{
ans*=Fast_power(p[i].val, min(p[i].Time,q[tail].Time) );
ans%=M;
}
}
int temp=ans;
printf("%d\n",temp);

return 0;
}


T3:小J的网红之路(SMOJ2062)

题目分析:这题的关键是理解好题意。(我个人理解的)题目的意思是说,我们要先将n个h分解成一些单词,然后再看一下这些单词中有多少个语法,输出可能达到的最多的语法数量,其中两个语法所使用的单词可以重叠。如果不能分解成一些单词,输出-1。

举例:

n=23,单词={5,7,11},语法={(2,2,2),(3,3,3)},答案=0;

此时用单词分解的方法只有5+7+11或7+5+11或11+5+7或11+7+5或7+5+11或7+11+5。而这些单词构不成语法。

n=7,单词={2},语法={(1,1,1)},答案=-1;

由于该句子无法用单词解读,输出-1。

n=5,单词={1,2},语法={(1,1,2),(1,2,1)},答案=2;

将句子解读为1,1,2,1,可以匹配2个语法。

那么接下来就好办了,我们先用一个时间为n*m的递推,算出长度为i(1<=i<=n)的句子能不能被单词解读,记为g[i],然后用f[i][j][k]表示长度为i的句子,最后两个单词长度为j,k的时候,最多能匹配多少个语法。我们可以枚举一个w,然后用f[i][j][k]去更新f[i+w][k][w],并看看有没有单词长度为{j,k,w}的语法,有则令f[i+w][k][w]再+1。统计答案的时候,如果g[n-i]为true,则用f[i][j][k]更新答案。

但这样的时间是O(n3)的,会超时。我们发现其实只有当j与k是某个语法后两个单词的长度的时候,f[i][j][k]才有可能更新答案。那我们不妨记f[i][j]表示长度为i的句子,最后一个语法是j所能匹配的最多语法数。这样我们可以枚举上一个语法k,看看k的后两个与j的前两个单词是否一样,或者k的后一个单词与j的前一个单词是否一样,然后用f[i-len][k]更新f[i][j],时间复杂度O(ne2)。

CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=1010;
const int maxm=110;
const int oo=1000000007;

int f[maxn][maxm];
bool g[maxn];
int len[maxn];

int a[maxm];
int b[maxm];
int c[maxm];

int n,m,e;

int Max(int x,int y)
{
return ((x>y)? x:y);
}

int main()
{
freopen("2062.in","r",stdin);
freopen("2062.out","w",stdout);

scanf("%d%d%d",&n,&m,&e);
for (int i=1; i<=m; i++) scanf("%d",&len[i]);
for (int i=1; i<=e; i++) scanf("%d%d%d",&a[i],&b[i],&c[i]);

g[0]=true;
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
if (i>=len[j])
g[i]|=g[ i-len[j] ];
if (!g
)
{
printf("-1\n");
return 0;
}

for (int i=0; i<=n; i++)
for (int j=0; j<=e; j++)
f[i][j]=-oo;
f[0][0]=0;

int ans=0;
for (int i=1; i<=n; i++)
for (int j=1; j<=e; j++)
{
int L=len[ a[j] ]+len[ b[j] ]+len[ c[j] ];
if (i<L) continue;
for (int k=0; k<=e; k++)
f[i][j]=Max(f[i][j],f[i-L][k]+1);

L-=len[ a[j] ];
for (int k=1; k<=e; k++)
if ( c[k]==a[j] )
f[i][j]=Max(f[i][j],f[i-L][k]+1);

L-=len[ b[j] ];
for (int k=1; k<=e; k++)
if ( b[k]==a[j] && c[k]==b[j] )
f[i][j]=Max(f[i][j],f[i-L][k]+1);

if ( g[n-i] ) ans=Max(ans,f[i][j]);
}
printf("%d\n",ans);

return 0;
}


总结:这次比赛还算正常发挥吧。主要是T3正确理解了题意(说实话T3的题面真的不清不楚),正解的DP也并不难。然后T2也并没有卡O(n−√log(n))的算法。下次要继续保持。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: