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 }
View Code

正解代码:

 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 }
View Code

 


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 }
View Code

 


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 }
View Code

正解代码:

 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 }
View Code

 


这次只是在本学校Rank1了,SDFZ的那个神仙比我高10分......(话说我怎么会犯一些这么蠢的错误呢)

「你说,那片云的背后,又会是什么呢?」
「大概是一望无际的晴空吧。」
「但是,这和我,又有什么关系呢?」
只是这样想着,悲伤便满溢出来。

(不是,为什么要加这段话?)

posted @ 2018-06-13 16:14  Cmd2001  阅读(288)  评论(0编辑  收藏  举报