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))的算法。下次要继续保持。
相关文章推荐
- sc2017新高二&高一模拟赛2 总结
- sc2017新高二&高一模拟赛7 总结
- sc2017新高二&高一模拟赛9 总结
- sc2017新高二&高一模拟赛1 总结
- sc2017新高二&高一模拟赛8 总结
- sc2017新高二&高一模拟赛4 总结
- sc2017新高二&高一模拟赛5 总结
- sc2017新高二&高一模拟赛3 总结
- sc2017新高二&高一模拟赛2 总结
- sc2017新高二&高一模拟赛7 总结
- 高二&高一模拟赛12 总结
- 高二&高一&初三模拟赛26 总结
- 高二&高一&初三模拟赛17 总结
- sc2017新高二&初三模拟赛11 总结
- 高二&高一&初三模拟赛20 总结
- 高二&高一&初三模拟赛24 总结
- 高二&高一模拟赛13 总结
- 高二&高一&初三模拟赛16 总结
- 高二&高一&初三模拟赛23 总结
- 高二&高一&初三模拟赛27 总结