比赛链接:这里  
  OP 
个人能力有限,有的题目实在知识点欠缺太多,可以参照一下其他大佬的全题解 。 
&感谢ph大佬 的全程指导。
  A 串 
递推。
  思路 
我们可以先算出长度为 1,2,…,n 的合法串的数量,再进行累加。
介于数据量(2 < n <= 106 ),通过排列组合来求几乎不可能,单纯打表可能就需要几十分钟。
接下来就是具体的递推过程:
假设长度为 n 的合法字符串有 An  个。长度为 n + 1 的字符串中的前 n 项可以分为两种情况:合法的与非法的。
——对于前 n 项为合法字符串的 n + 1 长字符串,显然情况有 An  * 26 种(即第 n + 1 项可以为任意字母); 
——对于前 n 项为非法字符串的 n + 1 长字符串,只有前 n 项存在 u 的字符串可以变为合法串,所以含 u 的 n 长字符串的数量为 26n  - 25n  个,其中有 An  个合法串,所以此种情况的 n + 1 长合法串有 26n  - 25n  - An  个(即存在 u 的 n 长非法字符串数量为 含 u 的 n 长字符串数量  减去 合法的 n 长字符串数量 ,该种串后接 s 即变为合法串)。
所以得到递推公式 An+1  = 25 *  An  + 26n  - 25n 
  代码 
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 #include  <bits/stdc++.h>  using  namespace  std;const  long  long  MOD = 1e9  + 7 ;long  long  qs (long  long  a,long  long  t)  {     long  long  ans=1 ;     while (t)     {         if (t&1 )ans=ans*a%MOD;         a=a*a%MOD;         t>>=1 ;     }     return  ans%MOD; }int  main ()  {     long  long  a=1 ,i,j,n,ans=1 ;cin>>n;     for (i=3 ;i<=n;i++)     {         a=a*25 +qs (26 ,i-1 )-qs (25 ,i-1 );         a%=MOD;         if (a<0 )a+=MOD;         ans+=a;         ans%=MOD;     }     printf ("%lld" ,ans);     return  0 ; }
 
  B 括号 
此题为特判题(Special Judge)
  思路 
首先我们观察样例,即可得知总括号对数为 每一个左括号后面的右括号总数的累加和。
所以说,为使总字符量尽可能少,我们需要把 k 先粗略地拆成 l1  * r1  的形式( l1  * r1  <= k ), l1  与 r1  需要尽可能接近(均值不等式);
我们可以取 l1  = ⌊ sqrt( k ) ⌋,则 r1  = ⌊ k / l1  ⌋,此时还需要补上差值 d = k - l1  * r1  ;
接下来就是构造了:我们可以依次输出 l1  个左括号,r1  - d 个右括号,1 个左括号,d 个右括号。此时总对数则为  l1  * r1  + d 。
要求是非空串,注意对 0 特判。
  代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include  <bits/stdc++.h>  using  namespace  std;int  main ()  {     int  k,l,r,d,i;     scanf ("%d" ,&k);     if (k==0 ){printf ("(" );return  0 ;}     l=(int )sqrt (k);     r=k/l;     d=k-r*l;     for (i=1 ;i<=l;i++)printf ("(" );     for (i=1 ;i<=r-d;i++)printf (")" );     printf ("(" );     for (i=1 ;i<=d;i++)printf (")" );     return  0 ; }
 
比赛时使用的另一种方法,即把 k 拆成 n1  * a1 2  +  n1  * a1 2  + … 的形式,再构造输出,代码如下,可ac,仅做参考。
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 #include  <bits/stdc++.h>  using  namespace  std;int  main ()  {     int  k,i,ma,j;     scanf ("%d" ,&k);     if (k==0 ){printf ("(" );return  0 ;}     int  a[31623 ]={0 };     ma=(int )sqrt (k);     while (k)     {         a[(int )sqrt (k)]++;         k-=(int )sqrt (k)*(int )sqrt (k);     }     for (i=1 ;i<=ma;i++)     {         printf ("(" );         if (a[i])         {             for (j=1 ;j<=i*a[i];j++)printf (")" );         }     }     return  0 ; }
 
  C 红和蓝 
dfs,涂色
  思路 
使用两遍bfs,第一遍进行分组和可行性判定,第二遍进行染色
具体实现上(也可以使用链表存图):
第一次dfs时,先递归处理下级节点,再将其与上级节点分入同一组,同时完成可行性判定;
第二次dfs时,判断其与上级节点是否在同一组,在即涂与上级相同的颜色,不在即将自己涂成与上级不同的颜色;
  代码 
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 #include  <bits/stdc++.h>  using  namespace  std;const  int  N = 2e5  + 5 ;struct  node {     int  to, next; }edge[N];int  head[N], cnt;int  mrk[N], col[N], num;bool  flag;void  add (int  u, int  v)  {     edge[++cnt] = {v, head[u]};     head[u] = cnt; }void  dfs1 (int  u, int  fa)  {     int  son = 0 ;     for (int  i = head[u]; i != -1 ; i = edge[i].next)     {         int  v = edge[i].to;         if (v != fa)         {         	son++;         	dfs1 (v, u);         }     }     if (son == 0  || mrk[u] == 0 )     {         if (mrk[fa] != 0 )         {             flag = 0 ;             return  ;         }         mrk[u] = mrk[fa] = ++num;     } }void  dfs2 (int  u, int  fa)  {     for (int  i = head[u]; i != -1 ; i = edge[i].next)     {         int  v = edge[i].to;         if (v == fa) continue ;         if (mrk[v] == mrk[u])             col[v] = col[u];         else  col[v] = !col[u];         dfs2 (v, u);     } }int  main ()  {     int  n;     cin >> n;     memset (head, -1 , sizeof (head));     for (int  i = 1 ; i < n; i++)     {         int  u, v;         scanf ("%d%d" ,&u,&v);         add (u, v);         add (v, u);     }     flag = true ;     dfs1 (1 , 0 );     if (flag == false  || mrk[0 ] != 0 )     {         printf ("-1" );         return  0 ;     }     dfs2 (1 , 0 );     for (int  i = 1 ; i <= n; i++)     {         printf ("%c" ,(col[i] == 0 )?'R' :'B' );     }     return  0 ; }
 
  D 点一成零 
bfs,并查集
  思路 
假设图中共有p组连通块,每一块包含c i c_i c i   格,则总方案数即为p ! ∗ ∏ i = 1 p c i p!*\prod_{i=1}^{p}c_i p ! ∗ ∏ i = 1 p  c i   ;
那么我们需要做的便是求出p与c i c_i c i   ;
我们可以用遍历网格进行bfs创建并查集,并用并查集维护更改;
最开始抱有侥幸心理,我想对于每一次更改,遍历所有连通块以重新计算ans,结果爆了TLE;
对于具体的维护过程: 
如果新增了一个面积为c的连通块,则a n s = a n s ∗ ( p + 1 ) ∗ c ans=ans*(p+1)*c a n s = a n s ∗ ( p + 1 ) ∗ c  ; 
进一步地,如果一个面积为b的连通块,由于方块更新,并入了一个面积为a的连通块,则对这两个连通块来说(即不包括点的那个块,不过该块同理,面积为1)a n s = a n s / p / a / b ∗ ( a + b ) ans=ans/p/a/b*(a+b) a n s = a n s / p / a / b ∗ ( a + b )  ;逆元处理除法即可;
使并查集的根为连通块中i ∗ n + j i*n+j i ∗ n + j  最小的点
  代码 
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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 #include  <bits/stdc++.h>  using  namespace  std;typedef  long  long  ll;const  ll M=1e9 +7 ,N=502 ; pair<int  ,int > fa[502 ][502 ]; map<pair<int ,int >,int >cnt; queue<pair<int ,int > >que;int  di[]={0 ,0 ,1 ,-1 },dj[]={1 ,-1 ,0 ,0 };int  mp[502 ][502 ],n,m,cou=0 ;ll qm (ll a,ll b)   {     ll ans=1 ;     for (;b;b>>=1 )     {if (b&1 )ans=ans*a%M;     a=a*a%M;}     return  ans; }pair<int ,int > find (pair<int ,int > pi)   {     if (fa[pi.first][pi.second]!=pi)fa[pi.first][pi.second]=find (fa[pi.first][pi.second]);     return  fa[pi.first][pi.second]; }void  bfs (pair<int ,int > fat)  {     int  k;     while (!que.empty ())     {         pair<int ,int >now=que.front ();         que.pop ();         for (k=0 ;k<4 ;k++)         {             int  in=now.first+di[k],jn=now.second+dj[k];             if (in>=0 &&in<n&&jn>=0 &&jn<n&&mp[in][jn]&&fa[in][jn]==make_pair (in,jn)&&!(in==fat.first&&jn==fat.second))             {                 cnt[fat]++;                 fa[in][jn]=fat;                 que.push ({in,jn});                              }         }     } }int  main ()  {     int  i,j,k;     string g;     cin>>n;     for (i=0 ;i<n;i++)     {         cin>>g;         for (j=0 ;j<n;j++)mp[i][j]=g[j]-'0' ,fa[i][j]={i,j};     }          ll ans=1 ;     for (i=0 ;i<n;i++)         for (j=0 ;j<n;j++)             if (mp[i][j]&&fa[i][j]==make_pair (i,j))             {                 cou++;                 cnt[{i,j}]=1 ;                 que.push ({i,j});                 bfs ({i,j});                 ans*=(ll)cou*cnt[{i,j}];                 ans%=M;             }     cin>>m;     while (m--)     {         int  ci,cj;         scanf ("%d%d" ,&ci,&cj);         if (mp[ci][cj])printf ("%lld\n" ,ans);         else          {             mp[ci][cj]=1 ;             cnt[{ci,cj}]=1 ;             cou++;             ans*=cou;             ans%=M;             pair<int ,int > to={ci,cj};             for (k=0 ;k<4 ;k++)             {                 int  in=ci+di[k],jn=cj+dj[k];                 if (in>=0 &&in<n&&jn>=0 &&jn<n&&mp[in][jn])                 {                     pair<int ,int >fan=find ({in,jn});                     if (fan.first*n+fan.second<to.first*n+to.second)to=fan;                 }             }             if (make_pair (ci,cj)!=to)             {                 pair<int ,int >fan=find ({ci,cj});                 fa[fan.first][fan.second]=to;                 ans*=qm ((ll)cou*(cnt[to]*cnt[fan])%M,M-2 );                 cou--;                 ans%=M;                 cnt[to]+=cnt[fan];                 ans*=cnt[to];                 ans%=M;             }             for (k=0 ;k<4 ;k++)             {                 int  in=ci+di[k],jn=cj+dj[k];                 if (in>=0 &&in<n&&jn>=0 &&jn<n&&mp[in][jn])                 {                     pair<int ,int >fan=find ({in,jn});                     if (fan!=to)                     {                         fa[fan.first][fan.second]=to;                         ans*=qm ((ll)cou*(cnt[to]*cnt[fan])%M,M-2 );                         cou--;                         ans%=M;                         cnt[to]+=cnt[fan];                         ans*=cnt[to];                         ans%=M;                     }                 }             }             printf ("%lld\n" ,ans);         }     }     return  0 ; }
 
  E 三棱锥之刻 
立体几何
  思路 
设中心点距面距离 ds ,距棱距离 da ,距顶点距离 dp ,喷漆半径 r 
分为四种情况:
① r < ds 
此种情况即无法喷到漆,面积为 0 ;
② ds <= r <= da 
此种情况即每面被喷漆形状均为完整的圆,通过勾股定理求漆面半径,求圆面积即可;
③ da < r < dp 
此种情况即为每面被喷漆形状为(三)弦切圆,如下图: 
我将其分为三个三角形和一个扇形求面积,因为图中 k l 的长度均可求,便可使用 arccos 求出角度,进而得知扇形所占圆心角,进而求出扇形面积;
④ r >= dp 
此时正四面体的四个面均被涂满,求三角形面积即可。
以下数据可参考,图源百度词条正四面体  
  代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <bits/stdc++.h>  using  namespace  std;#define  PI acos(-1) int  main ()  {     double  r,a,d;     cin>>a>>d;     if (d*d-a*a/24 <0 ) {printf ("0" );return  0 ;}     r=sqrt (d*d-a*a/24 );     if (r<=a/2 /sqrt (3 ))printf ("%.5lf" ,4 *PI*r*r);     else  if (r>a/2 /sqrt (3 )&&r<a/sqrt (3 ))     {         printf ("%.5lf" ,(3 *(sqrt (r*r-a*a/12 )*a/2 /sqrt (3 ))+PI*r*r*(2 *PI-6 *acos (a/2 /sqrt (3 )/r))/(2 *PI))*4 );     }     else  printf ("%.5lf" ,a*a*sqrt (3 ));     return  0 ; }
 
  F 对答案一时爽 
贪心
  思路 
最小值当然是 0 ,最大值即为两人答题总数 - 答案相异数。
假设一人全对,此时即为最大值。
  代码 
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 #include  <bits/stdc++.h>  using  namespace  std;int  main ()  {     int  n,sa=0 ,i,c=0 ;     char  a[202 ],b[202 ],g;     cin>>n;     getchar ();     c=0 ;     while (c!=n)     {         g=getchar ();         if (g>='A' &&g<='Z' )         a[c++]=g;     }     getchar ();     c=0 ;     while (c!=n)     {         g=getchar ();         if (g>='A' &&g<='Z' )         b[c++]=g;     }     for (i=0 ; i<n;i++)             if (a[i]==b[i])sa++;     printf ("%d %d" ,sa+n,0 );     return  0 ; }
 
字符串处理一团糟…
  H 幂塔个位数的计算 
找规律
  思路 
具体推倒过程过于繁琐,主要是通过a % 10 a\%10 a % 1 0  的结果推倒a a % 10 a^{a}\%10 a a % 1 0  的结果,再进行迭代。 
结果会在大概三层之内收束到定值。
  代码 
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 #include  <bits/stdc++.h>  using  namespace  std;typedef  long  long  ll;char  a[100005 ],n[100005 ];int  main ()  { 	scanf ("%s%s" ,a,n); 	ll la=strlen (a),ln=strlen (n); 	if (ln==1  && n[0 ]=='1' ) 	{ 		printf ("%d\n" ,a[la-1 ]-'0' ); 		return  0 ; 	} 	else  	{ 		int  a1=a[la-1 ]-'0' ; 		int  b=((a[la-2 ]-'0' )*10 +a[la-1 ]-'0' )%4 ; 		if (a1==0  || a1==1  || a1==5  || a1==6  || a1==9 ) 		{ 			printf ("%d\n" ,a1); 		} 		else  if (a1==4 ) 		{ 			printf ("6\n" ); 		} 		else  if (a1==2  || a1==8 ) 		{ 			if (ln==1  && n[0 ]=='2'  && b!=0 ) 			{ 				printf ("4\n" ); 			} 			else  			{ 				printf ("6\n" ); 			} 		} 		else  if (a1==3 ) 		{ 			if (b==1 ) 			{ 				printf ("3\n" ); 			} 			else  			{ 				printf ("7\n" ); 			} 		} 		else  if (a1==7 ) 		{ 			if (b==1 ) 			{ 				printf ("7\n" ); 			} 			else  if (b==3 ) 			{ 				printf ("3\n" ); 			} 		} 		return  0 ; 	} }
 
  I	限制不互素对的排列 
构造
  思路 
要用 [ 1 , n ] 的 n 个数构造出含 k ( k <= n / 2 ) 对非互质数对的数列,首先我们想到的就是用偶数构造所求部分,然后顺序排列其他数。
接下来除法默认向下取整 
具体来说,对于某偶数 2 * i ,一定与 2 * ( i + 1 ) 不互质;对于某奇数 2 * i + 1 ,一定与 2 * ( i + 1 )  + 1 互质;对于 >= 2 的正整数 i ,一定与 i + 1 互质。
依照以上三条性质,如果输入值为 8 3 我们便可以构造出 [ 2 , 4 , 6 , 8 , 1 , 3 , 5 , 7 ] 与此同时,便发现了一个问题:对于前 n 个数,含有 ⌊ n / 2 ⌋ 个偶数,最多构成 ⌊ n / 2 ⌋ - 1 个偶数对,但是 k <= n / 2 。
解决此种问题,我们可以进一步处理,选择满足以下性质的某偶数:可以表示为 2 * ( 2 * i + 1 ) ,即可以拆成 2 和某奇数的积。将此偶数放在偶数部分末尾,后再接该奇数,则可保证有 k 对非互质数对。
对于该偶数,10,14,18,…,34,…,122,… 均满足条件,但是满足条件的最小数是 6 。取最小数除了方便处理之外,同时也方便可行判定。
此时,本题的输入便有三种情况:
① n < 6 && k = n / 2 
此时无可行解,输出 -1;
② n > 6 && k < n / 2 
此时输出前 k + 1 个偶数后,其余数依次输出即可;
③ n > 6 && k = n / 2 
此时输出除 6 外的前 k - 1 个偶数后,输出 6 3 ,再依次输出其余数。
  代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include  <bits/stdc++.h>  using  namespace  std;int  main ()  {     int  n,k,i;     cin>>n>>k;     if (n<6 &&k==n/2 )printf ("-1" );     else  if (k==n/2 )     {         for (i=1 ;i<=n/2 ;i++)if (i!=3 )printf ("%d " ,2 *i);         printf ("6 3 1 " );         for (i=5 ;i<=n;i+=2 )printf ("%d " ,i);     }     else      {         for (i=1 ;i<=k+1 ;i++)printf ("%d " ,2 *i);         for (i=1 ;i<=n;i++){if (i%2 ==0 &&i/2 <=k+1 )continue ;printf ("%d " ,i);}     }     return  0 ; }
 
  J	一群小青蛙呱蹦呱蹦呱 
  思路 
我们手动模拟可知,剩下的数是质因子个数大于等于二的数,也就是说,剩下的数均可表示为 p1 ^m1  * p2 ^m2  * … * pn ^mn  ( n >= 2 )。如下图 
 
对于范围内的所有质数 p[ i ] ,第 j 个剩下的数可以表示为 p1 ^m1j  * p2 ^m2j  * … * pn ^mnj   则所有剩下的数的最小公倍数则为 p1 ^max( m11  , m12  , m13 , … , m1j  ) * p2 ^max( m21  , m22  , m23 , … , m2j  ) * … * pn ^max( mn1  , mn2  , mn3 , … , mnj ) 。 
接下来我们的任务就是求出 max() 。
对于 >= 2 的任意 p[ i ] ,小于 N (范围)且 含有 p[ i ] 幂次最大的 剩余数显然是 2 * p[ i ]^m ,则可以求出 m = logp[i] n/2;
对于质数 2 ,最大数则是 3 * 2m  ,此时 m = log2 n/3 。
之后累乘 p[ i ]m  即可。
  代码 
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 #include  <bits/stdc++.h>  using  namespace  std;const  long  long  MOD = 1e9  + 7 ;const  long  long  N=80000007 ;long  long  qs (long  long  a,long  long  t)  {     long  long  ans=1 ;     while (t)     {         if (t&1 )ans=ans*a%MOD;         a=a*a%MOD;         t>>=1 ;     }     return  ans%MOD; }static  int  p[N];static  bool  n[N];int  main ()  {     n[0 ]=1 ,n[1 ]=1 ;     int  i,j,c=0 ;     for (i=2 ;i<=N;i++)     {         if (!n[i])p[c++]=i;         for (j=0 ;j<c&&i*p[j]<=N;j++)         {             n[i*p[j]]=1 ;             if (i%p[j]==0 )break ;         }     }     int  n;     cin>>n;     if (n<6 ){printf ("empty" );return  0 ;}     long  long  ans=qs (2 ,(long  long )log2 (n/3.0 ));     for (i=1 ;p[i]<=n/2 &&i<c;i++)     {         ans%=MOD;         ans*=qs (p[i],(long  long )(log2 (n/2.0 )/log2 (p[i]*1.0 )));     }     printf ("%lld" ,ans%MOD);     return  0 ; }
 
附:关于线性筛的优化,可以参照这里 ,(代码未体现)
  ED 
《 基 础 选 手 》