2019牛客暑期多校训练营(第五场)

传送门

 

参考资料:

  [1]:官方题解(提取码:ppi6)

 

B.generator 1(矩阵快速幂)

•题意

  已知 $f_{i}=af_{i-1}+bf_{i-2}$;

  输入 f0,f1,a,b,n,mod;

  求 fn%mod ;

•题解

  矩阵快速幂入门习题;

  首先将递推式转化为矩阵乘法表达式:

  $\left( \begin{array}{cc} f_{i} \\ f_{i-1} \end{array}\right)=\left( \begin{array}{cc} a&b \\ 1&0 \end{array}\right)\cdot\left( \begin{array}{cc} f_{i-1} \\ f_{i-2} \end{array}\right)$

  不妨令 $\mathbf{A_{i}} =\left( \begin{array}{cc} f_{i} \\ f_{i-1} \end{array}\right)$ , $\mathbf{T} =\left( \begin{array}{cc} a&b \\ 1&0 \end{array}\right)$;

  那么,原式可化为 $A_{i}=T\cdot A_{i-1}=T^{2}\cdot A_{i-2}= \cdots = T^{i-1}\cdot A_{1}$;

  处理完递推式后,是不是就大功告成了?

  no,no,no;

  还差最后一步,如何处理 $T^{n-1}$ ?

  n 很大很大很大;

  考虑到秦九韶算法,将 n 分解;

  假设 n-1 = 3194;

  那么 n 可分解为 $n=\bigg(\Big(\big((3\times 10)+1\big)\times 10+9\Big)\times 10+4\bigg)$;

  在计算 $T^{3194}$ 时,可以按照字符串的位数进行处理;

  先计算 ans = T3;

  ans = ans10·T1;(ans=T31)

  ans = ans10·T9;(ans = T319)

  ans = ans10·T4;(ans = T3194)

•Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mem(a,b) memset(a,b,sizeof(a))
 4 #define ll long long
 5 const int N=5;
 6 const int maxn=1e6+50;
 7 
 8 int f[2];
 9 int a,b,mod;
10 char s[maxn];
11 struct Matrix
12 {
13     ll A[N][N];
14 
15     Matrix()
16     {
17         mem(A,0);
18     }
19     void Init()
20     {
21         for(int i=0;i < N;++i)
22             A[i][i]=1;///构造单位阵
23     }
24 }t,ans;
25 
26 Matrix mult(Matrix a,Matrix b)///a*b
27 {
28     Matrix tmp;
29     for(int i=1;i <= 2;++i)
30         for(int j=1;j <= 2;++j)
31             for(int k=1;k <= 2;++k)
32             {
33                 tmp.A[i][j] += a.A[i][k]*b.A[k][j]%mod;
34                 tmp.A[i][j] %= mod;
35             }
36     return tmp;
37 }
38 Matrix qPow(Matrix a,int b)///a^b
39 {
40     Matrix tmp;
41     tmp.Init();
42 
43     while(b)
44     {
45         if(b&1)
46             tmp=mult(tmp,a);
47         a=mult(a,a);
48         b >>= 1;
49     }
50     return tmp;
51 }
52 int Solve()
53 {
54     ans.Init();
55     t.A[1][1]=a;
56     t.A[1][2]=b;
57     t.A[2][1]=1;
58 
59     int len=strlen(s+1);
60     for(int i=len;;--i)
61     {
62         if(s[i] != '0')
63         {
64             s[i]--;
65             for(int j=i+1;j <= len;++j)
66                 s[j]='9';
67             break;
68         }
69     }
70 
71     for(int i=1;i <= len;++i)
72     {
73         ans=qPow(ans,10);///ans^10
74         ans=mult(ans,qPow(t,s[i]-'0'));///ans*t^(s[i]-'0')
75     }
76     return (ans.A[1][1]*f[1]%mod+ans.A[1][2]*f[0]%mod)%mod;
77 }
78 int main()
79 {
80     scanf("%d%d",f+0,f+1);
81     scanf("%d%d",&a,&b);
82     scanf("%s",s+1);
83     scanf("%d",&mod);
84 
85     printf("%d\n",Solve());
86 
87     return 0;
88 }
View Code

 

 


G.subsequence(DP)

•题意

  给你两个只包含字符 '0'~'9' ,长度分别为 n,m 的字符串 s,t;

  求 s 的子序列 s' 的个数,满足 s' > t;

•题解

  定义 dp[ i ][ j ] 表示 s 的前 i 个字符组成的子序列 > t1t2t3...tj 的子序列的总个数;

  首先,预处理出 dp[ i ][ 1 ];

  该如何处理呢?

  dp[ i ][ 1 ] = 2- 1 - illegal;

  illegal 指的是不合法的子序列个数,即 s 的前 i 个字符组成的所有的子序列中 < t1 的子序列个数;

  不合法的子序列主要包含两类:1.存在 si ≤ t1 , 2.以0开头的子序列;

  预处理出 dp[ i ][ 1 ] 后,接下来就是状态转移了;

    dp[ i ][ j ] = dp[ i-1][ j ] + dp[ i-1][ j-1];

  如果由 s 的前 i-1 个字符组成的子序列 s' 都满足 s' >  t1t2...tj 的话,那么肯定有 s'si > t1t2...tj ;

  如果由 s 的前 i-1 个字符组成的子序列 s' 都满足 s' >  t1t2...tj-1 的话,那么肯定有 s'si > t1t2...tj-1tj ;

  当然,如果 si ≤ tj ,上述两种情况便是 dp[ i ][ j ] 的全部情况;

  如果 si > tj 呢?

  那是不是就漏掉了“由 s 的前 i-1 个字符组成的子序列 s' 满足 s' ==  t1t2...tj”这种情况了?

  这种情况该如何处理呢?

  定义 $f_{i,j}$ 表示由s的前 i 位组成的子序列==t1t2...t组成的子序列的总个数;

  提前预处理出 $f_{i,j}$;

  然后,在处理 si > tj 的情况的时候,令 dp[ i ][ j ] 额外加上 f[ i-1][ j-1] 就好了;

•Code

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ll long long
  4 #define mem(a,b) memset(a,b,sizeof(a))
  5 const int MOD=998244353;
  6 const int maxn=3e3+50;
  7 
  8 int n,m;
  9 char s[maxn];
 10 char t[maxn];
 11 ll dp[maxn][maxn];///dp[i][j]:由s的前i位组成的子序列>t1,t2,...,tj组成的子序列的总个数
 12 ll f[maxn][maxn];///f[i][j]:由s的前i位组成的子序列==t1,t2,...,tj组成的子序列的总个数
 13 vector<int >v;
 14 
 15 ll qPow(ll a,ll b)
 16 {
 17     ll ans=1;
 18     a %= MOD;
 19 
 20     while(b)
 21     {
 22         if(b&1)
 23             ans=ans*a%MOD;
 24         a=a*a%MOD;
 25         b >>= 1;
 26     }
 27     return ans;
 28 }
 29 void initf()
 30 {
 31     for(int i=1;i <= n;++i)
 32     {
 33         f[i][1]=f[i-1][1];
 34         if(s[i] == t[1])
 35             f[i][1]++;
 36 
 37         f[i][1] %= MOD;
 38     }
 39     for(int j=2;j <= m;++j)
 40     {
 41         for(int i=1;i <= n;++i)
 42         {
 43             f[i][j]=f[i-1][j];
 44             if(s[i] == t[j])
 45                 f[i][j] += f[i-1][j-1];
 46 
 47             f[i][j] %= MOD;
 48         }
 49     }
 50 }
 51 void Initdp()
 52 {
 53     for(int i=1;i <= n;++i)
 54         dp[i][1]=(qPow(2,i)-1+MOD)%MOD;
 55 
 56     int cnt=0;
 57     v.clear();
 58     for(int i=1;i <= n;++i)
 59     {
 60         if(s[i] == '0')///记录所有s[i]=='0'的位置
 61             v.push_back(i);
 62 
 63         if(s[i] <= t[1])
 64             cnt++;
 65         dp[i][1] -= cnt;///去掉si <= t1 的个数
 66         dp[i][1]=(dp[i][1]+MOD)%MOD;
 67     }
 68     for(int i=1;i <= n;++i)///去掉前导0的个数
 69     {
 70         for(int j=0;j < v.size();++j)
 71         {
 72             if(v[j] >= i)
 73                 break;
 74             dp[i][1]=(dp[i][1]-(qPow(2,i-v[j])-1)+MOD)%MOD;
 75         }
 76     }
 77 }
 78 ll Solve()
 79 {
 80     for(int i=0;i <= n;++i)
 81         for(int j=0;j <= m;++j)
 82             f[i][j]=dp[i][j]=0;
 83 
 84     initf();///预处理f
 85     Initdp();///预处理dp[i][1]
 86 
 87     for(int i=2;i <= n;++i)
 88     {
 89         for(int j=2;j <= min(i,m);++j)
 90         {
 91             dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
 92             if(s[i] > t[j])
 93                 dp[i][j] += f[i-1][j-1];
 94             dp[i][j] %= MOD;
 95         }
 96     }
 97     return dp[n][m]%MOD;
 98 }
 99 int main()
100 {
101 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
102     int test;
103     scanf("%d",&test);
104     while(test--)
105     {
106         scanf("%d%d",&n,&m);
107         scanf("%s%s",s+1,t+1);
108 
109         printf("%lld\n",Solve());
110     }
111     return 0;
112 }
View Code

 

 

 


H.subsequence 2(拓扑序)

•题意

  有一个长度为 n 的隐藏字符串 s;

  s 中只包含小写英文字母的前 m 个字母;

  现在给你 $\frac{m\times (m-1)}{2}$ 对关系,每对关系给你一个只包含两种字符串;

  让你还原 s 使得其满足上述 $\frac{m\times (m-1)}{2}$ 对关系的相对位置;

  如果不存在这样的 s,输出 -1;

•题解

  

•Code

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define mem(a,b) memset(a,b,sizeof(a))
  4 const int maxn=1e4+50;
  5 
  6 int n,m;
  7 int cnt;
  8 int a[30];
  9 int in[maxn];
 10 char s[maxn];
 11 bool vis[30];
 12 vector<int >p[30];
 13 map<int ,char>f;
 14 int num;
 15 int head[maxn];
 16 struct Edge
 17 {
 18     int to;
 19     int next;
 20 }G[maxn*10];
 21 void addEdge(int u,int v)
 22 {
 23     G[num]=Edge{v,head[u]};
 24     head[u]=num++;
 25 }
 26 vector<int >ans;
 27 bool vis2[maxn];
 28 
 29 void DFS(int u)
 30 {
 31     ans.push_back(u);
 32     vis2[u]=true;
 33 
 34     for(int i=head[u];~i;i=G[i].next)
 35     {
 36         int to=G[i].to;
 37         if(!vis2[to])
 38             in[to]--;
 39     }
 40     for(int i=head[u];~i;i=G[i].next)
 41     {
 42         int to=G[i].to;
 43         if(!vis2[to] && !in[to])
 44             DFS(to);
 45     }
 46 }
 47 void Solve()
 48 {
 49     int x;
 50     for(int i=1;i <= cnt;++i)
 51         if(!in[i])
 52             x=i;
 53 
 54     mem(vis2,false);
 55     DFS(x);
 56 
 57     if(ans.size() != n)
 58     {
 59         puts("-1");
 60         return ;
 61     }
 62     
 63     for(int i=0;i < ans.size();++i)
 64         printf("%c",f[ans[i]]);
 65     printf("\n");
 66 }
 67 void Init()
 68 {
 69     num=0;
 70     mem(head,-1);
 71     mem(vis,false);
 72     mem(in,0);
 73 }
 74 int main()
 75 {
 76 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
 77     Init();
 78     scanf("%d%d",&n,&m);
 79 
 80     cnt=0;///莫名bug,cnt定义成局部的就出错
 81     for(int k=1;k <= m*(m-1)/2;++k)
 82     {
 83         char ch[2];
 84         int len;
 85         scanf("%s%d",ch,&len);
 86         if(len > 0)
 87             scanf("%s",s+1);
 88 
 89         for(int i=1;i <= len;++i)
 90         {
 91             int cur=s[i]-'a';
 92 
 93             if(vis[cur])
 94                 continue;
 95 
 96             p[cur].push_back(++cnt);
 97             f[cnt]=cur+'a';
 98         }
 99 
100         vis[ch[0]-'a']=true;
101         vis[ch[1]-'a']=true;
102 
103         a[ch[0]-'a']=0;
104         a[ch[1]-'a']=0;
105 
106         for(int i=1;i < len;++i)
107         {
108             int u=s[i]-'a';
109             int v=s[i+1]-'a';
110             
111             a[u]++;
112             addEdge(p[u][a[u]-1],p[v][a[v]]);
113 
114             in[p[v][a[v]]]++;
115         }
116     }
117     Solve();
118     return 0;
119 }
View Code

 

posted @ 2019-08-02 11:06  HHHyacinth  阅读(191)  评论(0编辑  收藏  举报