难度:medium
一开始是有打算逐位预处理 π \pi π ,然后发现精度炸了。看到数据范围 T ≤ 32 T\le 32 T ≤ 32 ,然后发现可以暴力,对于前面的每一位快速幂求出在第 n n n 位意义下的真分数,将分数所有值加起来取整就行,后面的位贡献不大,可以忽略。
1 2 3 4 5 6 7 8 REP (i,0 ,n){ res+=1.0 *qpow (16 ,n-i,(8 *i+1 )*16 ,4 )/(8 *i+1 ); res-=1.0 *qpow (16 ,n-i,(8 *i+4 )*16 ,2 )/(8 *i+4 ); res-=1.0 *qpow (16 ,n-i,(8 *i+5 )*16 ,1 )/(8 *i+5 ); res-=1.0 *qpow (16 ,n-i,(8 *i+6 )*16 ,1 )/(8 *i+6 ); while (res<0.0 )res+=16.0 ; while (res>=16.0 )res-=16.0 ; }
B - Bridge
难度:hard
场切的最难题,纪念一下。
我们称形如 ( x , y ) − ( x , y + 1 ) (x,y)-(x,y+1) ( x , y ) − ( x , y + 1 ) 的边为 横边 ,形如 ( 0 , y ) − ( 1 , y ) (0,y)-(1,y) ( 0 , y ) − ( 1 , y ) 的边为 竖边 。不难发现如果在 ( y , y + 1 ) (y,y+1) ( y , y + 1 ) 之间的两条竖边有一条断掉了,那么另外一条必然为桥。
扩展一下,对于最靠近任意一个 y y y 左边的横边 L L L 和最靠近 y + 1 y+1 y + 1 右边的横边 R R R 所代表的区间 [ L , R ] [L,R] [ L , R ] 中的所有横边,断边的数量最多只能有一条,否则图不连通。当断边数为 0 0 0 时,所有边都不是桥边,因为会成一个 [ L , R ] [L,R] [ L , R ] 环;当断边数为 1 1 1 时,桥边的数量为 2 ( R − L ) − 1 2(R-L)-1 2 ( R − L ) − 1 。
这下我们可以快速判断横边是否为桥了。接下来是竖边。设存在竖边 x x x ,靠近的最左、右竖边分别为 L , R L,R L , R ,那么竖边 x x x 为桥边的充要条件是 [ L , x ] [L,x] [ L , x ] 和 [ x , R ] [x,R] [ x , R ] 中的所有横边中各有一条是断边,否则成环就不是桥边了。
综上所述,我们需要维护所有的竖边和快速求区间横边数量。分别用 set \text{set} set 和 Fenwick Tree \text{Fenwick Tree} Fenwick Tree 维护即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 set<int >s; int getL (int x) { auto it=s.lower_bound (x);--it; return *it; } int getR (int x) { auto it=s.upper_bound (x); return *it; } int query1 (int x) { if (x==1 ||x==n)return 0 ; int L=getL (x),R=getR (x); if (!query_bit (L,x-1 ))return 0 ; if (!query_bit (x,R-1 ))return 0 ; return 1 ; } int query2 (int l,int r) { int res=0 ; res+=query1 (l); res+=query1 (r); if (query_bit (l,r-1 ))res+=(r-l)*2 -1 ; return res; } void solve () { n=read ()+2 ,m=read (); init_bit (); add_bit (1 ,1 ),add_bit (n-1 ,1 ); s.clear (); REP (i,1 ,n)s.insert (i); int res=0 ; REP (i,1 ,m){ int tp=read (),x1=read (),y1=read ()+1 ,x2=read (),y2=read ()+1 ; if (y1>y2)swap (y1,y2); int L=getL (y2),R=getR (y1); if (tp==1 ){ if (x1==x2){ res-=query2 (L,R); add_bit (y1,1 ); res+=query2 (L,R); }else { res-=query2 (L,R); s.insert (y1); res+=query2 (L,y1); res+=query2 (y1,R); res-=query1 (y1); } }else { if (x1==x2){ res-=query2 (L,R); add_bit (y1,-1 ); res+=query2 (L,R); }else { res-=query2 (L,y1); res-=query2 (y1,R); res+=query1 (y1); s.erase (s.find (y1)); res+=query2 (L,R); } } writeln (res); } }
F - Heron and His Triangle
难度:easy
好笑吗,打表题。
根据海伦公式 S = p ( p − a ) ( p − b ) ( p − c ) , p = a + b + c 2 S=\sqrt{p(p-a)(p-b)(p-c)},p=\frac{a+b+c}{2} S = p ( p − a ) ( p − b ) ( p − c ) , p = 2 a + b + c ,可以得出面积关于 t t t 的等式是 t 4 3 ( t 2 − 4 ) \frac{t}{4}\sqrt{3(t^2-4)} 4 t 3 ( t 2 − 4 ) 。干眼瞪不出什么规律,然后打完表发现是一个二阶递推数列。除了前两项 4 , 14 4,14 4 , 14 ,后面项的递推公式是 A n = 4 A n − 1 − A n − 2 A_n=4A_{n-1}-A_{n-2} A n = 4 A n − 1 − A n − 2 。然后直接把这个数列递推打表出来,每一个询问暴力查询即可。
G - Infinite Fraction Path
难度:medium
这不是我们 SA 基数排序的倍增法嘛!怎么出板子,而且似乎两只 log \log log 就可以跑过,所以可以把我经常写错的 基排扔掉。
对于没有接触过 SA 的同学,讲讲倍增法是怎样运行的。先处理出每一个节点长度为 1 1 1 的字符串的排名,然后下一步求出长度为 2 2 2 的字符串的排名,具体地,相当于把一些两个长度为 1 1 1 的,已经排名过的字符串接起来然后字典序排序。唉,这不就是双关键字排序嘛!前面的字符串第一关键字,后面字符串第二关键字,然后排序完重新更新一下每个节点出发长度为 2 2 2 字符串的排名。
扩展一下,对于节点 i i i ,设移动 2 k 2^k 2 k 步后的节点为 ( i , k ) (i,k) ( i , k ) ,从 i i i 出发的长度为 2 k 2^k 2 k 的字符串为 S ( i , k ) S(i,k) S ( i , k ) 。已经求出了 S ( i , k − 1 ) S(i,k-1) S ( i , k − 1 ) 的排名,要求 S ( i , k ) S(i,k) S ( i , k ) 的排名,即将两个字符串 S ( i , k − 1 ) , S ( ( i , k − 1 ) , k − 1 ) S(i,k-1),S((i,k-1),k-1) S ( i , k − 1 ) , S (( i , k − 1 ) , k − 1 ) 首尾相接在一起。排序的时候根据 r k S ( i , k − 1 ) rk_{S(i,k-1)} r k S ( i , k − 1 ) 为第一关键字,r k S ( ( i , k − 1 ) , k − 1 ) rk_{S((i,k-1),k-1)} r k S (( i , k − 1 ) , k − 1 ) 为第二关键字进行排序,就可以求出 r k S ( i , k ) rk_{S(i,k)} r k S ( i , k ) 了。然后 k k k 可以取到 ⌈ lg n ⌉ \lceil\lg n\rceil ⌈ lg n ⌉ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 REP (i,0 ,n-1 )st[0 ][i]=(1ll *i*i+1 )%n;REP (i,0 ,n-1 )f[i]=s[i];REP (j,1 ,K-1 ){ REP (i,0 ,n-1 ){ st[j][i]=st[j-1 ][st[j-1 ][i]]; a[i+1 ]=node{f[i],f[st[j-1 ][i]],i}; } sort (a+1 ,a+n+1 ); int rk=0 ; REP (i,1 ,n){ int id=a[i].id; ++rk; if (a[i].v1==a[i-1 ].v1&&a[i].v2==a[i-1 ].v2)--rk; g[id]=rk; } REP (i,0 ,n-1 )f[i]=g[i]; } int p=0 ;REP (i,1 ,n-1 )if (f[i]==1 )p=i; REP (i,1 ,n){ putchar (s[p]); p=st[0 ][p]; }
H - Legends of the Three Kingdoms
难度:medium
难点在于模拟的概率题。预处理所有状态倒过来转移,转移全是 Θ ( 1 ) \Theta(1) Θ ( 1 ) 的;询问直接 Θ ( 1 ) \Theta(1) Θ ( 1 ) 查询。直接上代码吧,思路没啥好讲的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 const double eps=1e-16 ; double f[N][N][N][N][4 ][3 ];int id[4 ]={0 ,3 ,2 ,1 };int main () { int n=40 ; REP (a,0 ,n)REP (b,0 ,n)REP (c,0 ,n)REP (d,0 ,n)REP (x,0 ,3 ){ int i=id[x]; int j=(i+1 )&3 ; double maxn=-0.5 ;double sum=0 ; if (!a){ if (!b&&!d){ REP (p,0 ,2 )f[a][b][c][d][i][p]=-1.0 ; }else if (!b&&!c){ f[a][b][c][d][i][0 ]=0.0 ; f[a][b][c][d][i][1 ]=0.0 ; f[a][b][c][d][i][2 ]=1.0 ; }else { f[a][b][c][d][i][0 ]=0.0 ; f[a][b][c][d][i][1 ]=1.0 ; f[a][b][c][d][i][2 ]=0.0 ; } continue ; } if (!b&&!d){ f[a][b][c][d][i][0 ]=1.0 ; f[a][b][c][d][i][1 ]=0.0 ; f[a][b][c][d][i][2 ]=0.0 ; continue ; } if (!b&&i==1 ||!c&&i==2 ||!d&&i==3 ){ memcpy (f[a][b][c][d][i],f[a][b][c][d][j],sizeof (f[a][b][c][d][j])); continue ; } auto A1=[&](int v){ if (a&&f[a-1 ][b][c][d][j][v]>maxn-eps)maxn=f[a-1 ][b][c][d][j][v];}; auto B1=[&](int v){ if (b&&f[a][b-1 ][c][d][j][v]>maxn-eps)maxn=f[a][b-1 ][c][d][j][v];}; auto C1=[&](int v){ if (c&&f[a][b][c-1 ][d][j][v]>maxn-eps)maxn=f[a][b][c-1 ][d][j][v];}; auto D1=[&](int v){ if (d&&f[a][b][c][d-1 ][j][v]>maxn-eps)maxn=f[a][b][c][d-1 ][j][v];}; auto A2=[&](int v){ if (a&&f[a-1 ][b][c][d][j][v]>maxn-eps)sum+=1.0 ;}; auto B2=[&](int v){ if (b&&f[a][b-1 ][c][d][j][v]>maxn-eps)sum+=1.0 ;}; auto C2=[&](int v){ if (c&&f[a][b][c-1 ][d][j][v]>maxn-eps)sum+=1.0 ;}; auto D2=[&](int v){ if (d&&f[a][b][c][d-1 ][j][v]>maxn-eps)sum+=1.0 ;}; auto A3=[&](int v){ if (a&&f[a-1 ][b][c][d][j][v]>maxn-eps)REP (p,0 ,2 ) f[a][b][c][d][i][p]+=1.0 /sum*f[a-1 ][b][c][d][j][p];}; auto B3=[&](int v){ if (b&&f[a][b-1 ][c][d][j][v]>maxn-eps)REP (p,0 ,2 ) f[a][b][c][d][i][p]+=1.0 /sum*f[a][b-1 ][c][d][j][p];}; auto C3=[&](int v){ if (c&&f[a][b][c-1 ][d][j][v]>maxn-eps)REP (p,0 ,2 ) f[a][b][c][d][i][p]+=1.0 /sum*f[a][b][c-1 ][d][j][p];}; auto D3=[&](int v){ if (d&&f[a][b][c][d-1 ][j][v]>maxn-eps)REP (p,0 ,2 ) f[a][b][c][d][i][p]+=1.0 /sum*f[a][b][c][d-1 ][j][p];}; if (i==0 ){ B1 (0 ),D1 (0 ); B2 (0 ),D2 (0 ); B3 (0 ),D3 (0 ); }else if (i==1 ){ A1 (1 ),C1 (1 ),D1 (1 ); A2 (1 ),C2 (1 ),D2 (1 ); A3 (1 ),C3 (1 ),D3 (1 ); }else if (i==2 ){ B1 (0 ),D1 (0 ); B2 (0 ),D2 (0 ); B3 (0 ),D3 (0 ); }else if (i==3 ){ A1 (2 ),B1 (2 ),C1 (2 ); A2 (2 ),B2 (2 ),C2 (2 ); A3 (2 ),B3 (2 ),C3 (2 ); } } int T=read (); while (T--){ int a=read (),c=read (),b=read (),d=read (); printf ("%.6lf %.6lf %.6lf\n" ,f[a][b][c][d][0 ][0 ],f[a][b][c][d][0 ][1 ],f[a][b][c][d][0 ][2 ]); } return 0 ; }
I - Little Boxes
难度:simple
不喜欢 __int128 的小朋友你们好啊,我是(请输入文本)。
K - Rabbits
难度:simple
咋要特判 ( 1 , 2 ) (1,2) ( 1 , 2 ) 和 ( n − 1 , n ) (n-1,n) ( n − 1 , n ) 有一个区间绝对够不到的情况,吓死我了,差点没测样例就交了。
L - Tree
难度:easy
简单结论题,评黄很合理吧。
结论:一个点与题目所求交集的边有公共部分,当且仅当以这个点为根时,存在两个不同儿子的子树大小 ≤ k \le k ≤ k ,可以有一个子树包含该节点。
必要性大家都会证,如果不存在两个不同儿子的子树大小 ≤ k \le k ≤ k ,那么这个点就一定没有任何相邻边被 k k k 个颜色经过。接下来是充分性,感性理解一下,将不满足结论的点称作关键点 ,满足条件的,且与至少一个关键点相邻的非关键点称作临界点 。将所有临界点(如果必须要【有一个子树包含该节点】,那么同时染上该临界点)所连的关键点子树染上 k k k 种颜色,那么所有关键点的 s z ≥ k sz\ge k sz ≥ k 的方向均至少存在一个临界点及其关键点子树染上了 k k k 种颜色。构造成立。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void dfs (int u,int fa) { sz[u]=1 ; int sum=0 ; for (auto v:E[u]){ if (v==fa)continue ; dfs (v,u); sum+=sz[v]>=m-1 ; sum+=sz[v]>=m-1 &&n-sz[v]>=m; sz[u]+=sz[v]; } sum+=(n-sz[u])>=m-1 ; sum+=(n-sz[u])>=m-1 &&sz[u]>=m; if (sum>=2 )++res; }
M - Wandering Robots
难度:medium
镜像赛场上没切出来的人均题之一。
设在 ( x , y ) (x,y) ( x , y ) 停留的概率为 f x , y f_{x,y} f x , y ,( x , y ) (x,y) ( x , y ) 可以通往的相邻点有有 d x , y d_{x,y} d x , y 个,那么显然有 f x , y = f x , y d x , y + 1 + f x − 1 , y d x − 1 , y + 1 + f x + 1 , y d x + 1 , y + 1 + f x , y − 1 d x , y − 1 + 1 + f x , y + 1 d x , y + 1 + 1 f_{x,y}=\frac{f_{x,y}}{d_{x,y}+1}+\frac{f_{x-1,y}}{d_{x-1,y}+1}+\frac{f_{x+1,y}}{d_{x+1,y}+1}+\frac{f_{x,y-1}}{d_{x,y-1}+1}+\frac{f_{x,y+1}}{d_{x,y+1}+1} f x , y = d x , y + 1 f x , y + d x − 1 , y + 1 f x − 1 , y + d x + 1 , y + 1 f x + 1 , y + d x , y − 1 + 1 f x , y − 1 + d x , y + 1 + 1 f x , y + 1 。然后还注意到 ∑ f x , y = 1 \sum f_{x,y}=1 ∑ f x , y = 1 ,所以可以联立一个概率方程组,解这个方程组就行。
然后观察样例,会发现一个很有趣的结论,概率即为目标点集的出度和/总点集的出度和。有些佬用这个方法写出来了但是不会证,但是你会发现,只要满足上面的概率方程组就行了,而这是易证的。
然后会发现相对于正常的图而言,被截掉的出度只与 k k k 有关,所以我们暴力找到这些点,去重一下就可以算出实际出度了。