20180613小测
这题真是OI题?
T1:
就是给你一个不明觉厉的递推式然后计算东西啦。
首先我们能用long double写一个暴力,发现前几项越来越小,后几项越来越大,明显不对......
考虑为什么,因为计算机中的浮点数是有误差的,这样推的话,误差会被放大到n!级,直接GG!
应该怎样做?手玩了半天发现没什么通项公式,难道写一个带压位FFT能做除法的高精度小数类?然后再求一个3w位有效数字的e?人干事,弃坑弃坑......
于是写了10分暴力。由于没有删调试语句爆了零。
考虑正解怎么做(抄题解?):
首先我们有一个结论:
于是我们可以这样从后向前递推f(n),不存在精度误差。
(SDFZ某神仙:我观察到这东西绝对值越来越小,于是可以令f(1e5)=0,然后逆推f(n),于是就这样AC了此题)
如何证明这个结论?
首先我们先构造一个积分:
考虑如何计算?首先我们有:
我们令u,v等于如下值,则有:
我们钦定g,h为如下意义:
则有:
没错,这东西就是f了。
此外我们有:
两边同时求积分,可得:
也就是:
于是我们就证明完了......
(话说这真不是数竞巨佬的来强行出题了?)
考场爆零代码:
1 #include<cstdio> 2 #include<cmath> 3 typedef long double ldb; 4 const int maxn=1e4+1e2; 5 const ldb e = exp(1); 6 7 ldb f[maxn]; 8 9 int main() { 10 static int n; 11 scanf("%d",&n) , *f = 1 - 1 / e; 12 for(int i=1;i<=n;i++) f[i] = 1 - i * f[i-1]; 13 printf("%0.4Lf\n",120/e-44); 14 printf("%0.4Lf\n",f[n]); 15 return 0; 16 }
正解代码:
1 #include<cstdio> 2 #include<cmath> 3 typedef long double ldb; 4 using namespace std; 5 const int maxn=1e5+1e2,lim=1e5; 6 const ldb e = exp(1); 7 8 ldb f[maxn]; 9 10 inline void work() { 11 f[lim] = ( 1 / ( lim + 1 ) + 1 / ( ( lim + 1 ) * e ) ) / 2.0; 12 for(int i=lim;i;i--) f[i-1] = ( 1 - f[i] ) / i; 13 } 14 15 int main() { 16 static int n; 17 scanf("%d",&n) , work() , printf("%0.4Lf\n",f[n]); 18 return 0; 19 }
T2:
这题什么鬼......
首先不共线的三点确定一个平面,那么我们能找不共线的三个点,钦定第一个点坐标为(0,0),第二个点在x轴上,第三个点纵坐标非负,这样就确定平面了。
然后轻松地解方程求出其他所有点的坐标,因为题目保证有解,不会存在冲突的。
但是细节很多啊......
首先对于重合的点,我们要先用一个并查集把它们缩起来,否则如果第一个点和第二个点重合了,这题GG。
此外第二个和第三个点坐标的计算方法是要单独写的......
另外,如果前三个基准点共线的话,我们需要直接重选第三个基准点(显然不用重新计算共线那个点的坐标,因为它的位置是确定的)。
然后就可以AC啦!
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #define debug cerr 7 typedef long double ldb; 8 using namespace std; 9 const int maxn=1e2+1e1; 10 const ldb eps = 1e-4; 11 12 inline ldb sq(const ldb &x) { 13 return x * x; 14 } 15 ldb in[maxn][maxn],x[maxn],y[maxn]; 16 int n,sec,thr; 17 18 inline void calc_second() { 19 x[sec] = in[1][sec]; 20 } 21 inline void calc_third() { 22 const ldb r1 = in[1][thr] , r2 = in[sec][thr] , rt = in[1][sec]; 23 const ldb rit = sq(r2) - sq(r1) - sq(rt); 24 x[thr] = rit / ( -2 * x[sec] ) , y[thr] = sqrt( sq(r1) - sq(x[thr]) ); 25 } 26 inline void calc_kth(int k) { 27 const ldb r1 = in[1][k] , r2 = in[sec][k] , r3 = in[thr][k]; 28 const ldb rit = sq(r2) - sq(r1) - sq(in[1][sec]); 29 x[k] = rit / ( -2 * x[sec] ); 30 const ldb rit2 = sq(r3) - sq(r1) - sq(in[1][thr]) + 2 * x[thr] * x[k]; 31 y[k] = rit2 / ( -2 * y[thr] ); 32 } 33 inline bool same_line() { 34 const ldb dx2 = x[sec] - x[1] , dy2 = y[sec] - y[1]; 35 const ldb dx3 = x[thr] - x[1] , dy3 = y[thr] - y[1]; 36 if( fabs( dx2 * dy3 - dx3 * dy2 ) <= eps ) return 1; 37 return 0; 38 } 39 40 struct UnionFindSet { 41 int fa[maxn]; 42 inline int findfa(int x) { 43 return fa[x] == x ? x : fa[x] = findfa(fa[x]); 44 } 45 inline void merge(int x,int y) { 46 if( findfa(x) != findfa(y) ) fa[findfa(y)] = findfa(x); 47 } 48 inline void init() { 49 for(int i=1;i<=n;i++) fa[i] = i; 50 } 51 }ufs; 52 53 inline ldb dis(int i,int j) { 54 return sqrt(sq(x[ufs.findfa(i)]-x[ufs.findfa(j)])+sq(y[ufs.findfa(i)]-y[ufs.findfa(j)])); 55 } 56 57 int main() { 58 scanf("%d",&n) , ufs.init(); 59 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { 60 scanf("%Lf",in[i]+j); 61 if( i < j && fabs(in[i][j]) <= eps ) ufs.merge(i,j); 62 } 63 for(int i=2;i<=n;i++) if( ufs.findfa(i) == i ) { 64 if( !sec ) sec = i; 65 else if( !thr ) {thr = i; break;} 66 } 67 if( sec ) calc_second(); 68 if( thr ) calc_third(); 69 for(int i=thr+1;i<=n;i++) if( ufs.findfa(i) == i ) { 70 if( same_line() ) thr = i , calc_third(); 71 else calc_kth(i); 72 } 73 for(int i=1;i<=n;i++) printf("%Lf %Lf\n",x[ufs.findfa(i)],y[ufs.findfa(i)]); 74 return 0; 75 }
T3:
题目描述就是给你一个矩阵的第一行,让你构造一个值域满足要求的矩阵使其行列式为1。
由于矩阵转置行列式不变,我们可以先把第一行转置为第一列,最后再转置回来。
由于题目保证存在两个数gcd为1,那么我们可以枚举是哪两个数的gcd为1,用辗转相除消元把矩阵的第一列消成仅有一个1。
然后再枚举对角线上的值是+1还是-1,把消元的过程逆回去,看值域是否满足要求。
为什么要两遍枚举?为了保证不把有解的情况由于值域问题判定为无解。
于是这样写就能获得90分啦!
(为什么少10分?因为我有一个地方的swap没有应用上。话说这样才只扣10分)
考场90分代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 const int maxn=7,lim=2e3; 10 11 inline lli gcd(lli x,lli y) { 12 if( !x || !y ) return x | y; 13 register lli t; 14 while( ( t = x % y ) ) x = y , y = t; 15 return y; 16 } 17 18 lli in[maxn],way[maxn],ans[maxn][maxn]; 19 lli ope[maxn*maxn*maxn],sou[maxn*maxn*maxn],tar[maxn*maxn*maxn],mul[maxn*maxn*maxn]; 20 int n,fs,cnt,lastnum; 21 22 inline bool judge() { 23 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if( abs(ans[i][j]) > lim ) return 0; 24 return 1; 25 } 26 inline bool solve_sta(int ss) { 27 memset(ans,0,sizeof(ans)); 28 int pi = 1; ans[1][1] = 1; 29 for(int i=2;i<=n;i++) { 30 if( ss & ( 1 << ( i - 2 ) ) ) ans[i][i] = -1; 31 else ans[i][i] = 1; 32 pi *= ans[i][i]; 33 } 34 if( pi != lastnum ) return 0; 35 for(int i=cnt;i;i--) { 36 if( ope[i] == 1 ) { // swap 37 for(int j=1;j<=n;j++) swap(ans[sou[i]][j],ans[tar[i]][j]); 38 } else { 39 for(int j=1;j<=n;j++) ans[tar[i]][j] += ans[sou[i]][j] * mul[i]; 40 } 41 } 42 return judge(); 43 } 44 inline bool getway() { 45 static int lasti=1,lastj=0; 46 cnt = 0 , lastnum = 1; 47 for(int i=1;i<=n;i++) way[i] = in[i]; 48 for(;lasti<=n;lasti++,lastj=0) { 49 for(lastj++;lastj<=n;lastj++) if( lasti != lastj && gcd(in[lasti],in[lastj]) == 1 ) { 50 while( way[lasti] && way[lastj] ) { 51 if( way[lasti] > way[lastj] ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = lastj , lastnum *= -1 , swap(way[lasti],way[lastj]); 52 int t = way[lastj] / way[lasti]; 53 ope[++cnt] = 0 , sou[cnt] = lasti , tar[cnt] = lastj , mul[cnt] = t , way[lastj] %= way[lasti]; 54 } 55 if( !way[lasti] ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = lastj , lastnum *= -1 , swap(way[lasti],way[lastj]); 56 if( lasti != 1 ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = 1 , lastnum *= 1; 57 for(int j=2;j<=n;j++) if( way[j] ) ope[++cnt] = 0 , sou[cnt] = 1 , tar[cnt] = j , mul[cnt] = way[j] , way[j] = 0; 58 return 1; 59 } 60 } 61 return 0; 62 } 63 64 int main() { 65 scanf("%d",&n) , fs = 1 << ( n - 1 ); 66 for(int i=1;i<=n;i++) scanf("%lld",in+i); 67 for(int i=1;i<=n;i++) debug<<in[i]<<" "; debug<<endl; 68 while(getway()) for(int ss=0;ss<fs;ss++) if( solve_sta(ss) ) { 69 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) printf("%lld%c",ans[j][i],j!=n?' ':'\n'); 70 return 0; 71 } 72 puts("no solution"); 73 return 0; 74 }
正解代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 const int maxn=7,lim=2e3; 10 11 inline lli gcd(lli x,lli y) { 12 if( !x || !y ) return x | y; 13 register lli t; 14 while( ( t = x % y ) ) x = y , y = t; 15 return y; 16 } 17 18 lli in[maxn],way[maxn],ans[maxn][maxn]; 19 lli ope[maxn*maxn*maxn],sou[maxn*maxn*maxn],tar[maxn*maxn*maxn],mul[maxn*maxn*maxn]; 20 int n,fs,cnt,lastnum; 21 22 inline bool judge() { 23 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if( abs(ans[i][j]) > lim ) return 0; 24 return 1; 25 } 26 inline bool solve_sta(int ss) { 27 memset(ans,0,sizeof(ans)); 28 int pi = 1; ans[1][1] = 1; 29 for(int i=2;i<=n;i++) { 30 if( ss & ( 1 << ( i - 2 ) ) ) ans[i][i] = -1; 31 else ans[i][i] = 1; 32 pi *= ans[i][i]; 33 } 34 if( pi != lastnum ) return 0; 35 for(int i=cnt;i;i--) { 36 if( ope[i] == 1 ) { // swap 37 for(int j=1;j<=n;j++) swap(ans[sou[i]][j],ans[tar[i]][j]); 38 } else { 39 for(int j=1;j<=n;j++) ans[tar[i]][j] += ans[sou[i]][j] * mul[i]; 40 } 41 } 42 return judge(); 43 } 44 inline bool getway() { 45 static int lasti=1,lastj=0; 46 cnt = 0 , lastnum = 1; 47 for(int i=1;i<=n;i++) way[i] = in[i]; 48 for(;lasti<=n;lasti++,lastj=0) { 49 for(lastj++;lastj<=n;lastj++) if( lasti != lastj && gcd(in[lasti],in[lastj]) == 1 ) { 50 while( way[lasti] && way[lastj] ) { 51 if( way[lasti] > way[lastj] ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = lastj , lastnum *= -1 , swap(way[lasti],way[lastj]); 52 int t = way[lastj] / way[lasti]; 53 ope[++cnt] = 0 , sou[cnt] = lasti , tar[cnt] = lastj , mul[cnt] = t , way[lastj] %= way[lasti]; 54 } 55 if( !way[lasti] ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = lastj , lastnum *= -1 , swap(way[lasti],way[lastj]); 56 if( lasti != 1 ) ope[++cnt] = 1 , sou[cnt] = lasti , tar[cnt] = 1 , lastnum *= -1 , swap(way[lasti],way[1]); 57 for(int j=2;j<=n;j++) if( way[j] ) ope[++cnt] = 0 , sou[cnt] = 1 , tar[cnt] = j , mul[cnt] = way[j] , way[j] = 0; 58 return 1; 59 } 60 } 61 return 0; 62 } 63 64 int main() { 65 scanf("%d",&n) , fs = 1 << ( n - 1 ); 66 for(int i=1;i<=n;i++) scanf("%lld",in+i); 67 while(getway()) for(int ss=0;ss<fs;ss++) if( solve_sta(ss) ) { 68 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) printf("%lld%c",ans[j][i],j!=n?' ':'\n'); 69 return 0; 70 } 71 puts("no solution"); 72 return 0; 73 }
这次只是在本学校Rank1了,SDFZ的那个神仙比我高10分......(话说我怎么会犯一些这么蠢的错误呢)
「你说,那片云的背后,又会是什么呢?」
「大概是一望无际的晴空吧。」
「但是,这和我,又有什么关系呢?」
只是这样想着,悲伤便满溢出来。
(不是,为什么要加这段话?)