zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第四场)BCFH

    BCFH 

    B. Basic God Problem

    题意

    给出c和n,求fc(n)。

    题解

    递归到最后 fc 函数肯定等于1,那么就变成了求c被乘了几次,只要找到 x 最多能被分解成多少个数相乘就好了。预处理用线性筛求出每个数最多能被分解成多少个数相乘,快速幂求出解。

    代码

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define pb push_back
     4 #define ft first
     5 #define sd second
     6 #define pii pair<int,int>
     7 #define pll pair<ll,ll>
     8 using namespace std;
     9  
    10 ll st[1001000];
    11 ll prime[1001000];
    12 ll d[1001000];
    13 ll cnt;
    14 const ll mod=1e9+7;
    15 void Prime(ll n)
    16 {
    17     cnt=0;
    18     for(ll i=2;i<=n;i++){
    19         if(!st[i]) prime[cnt++]=i,d[i]=1;
    20         for(int j=0;prime[j]*i<=n;j++){
    21             st[prime[j]*i]=1;
    22             d[prime[j]*i]=d[i]+1;
    23             if(i%prime[j]==0) break;
    24         }
    25     }
    26 }
    27 ll quick(ll a,ll b)
    28 {
    29     ll res=1;
    30     a=a%mod;
    31     while(b){
    32         if(b&1) res=(res*a)%mod;
    33         a=(a*a)%mod;
    34         b>>=1;
    35     }
    36     return res%mod;
    37 }
    38 int main()
    39 {
    40     ios::sync_with_stdio(false);
    41     cin.tie(0);
    42     cout.tie(0);
    43     Prime(1000000);
    44     for(ll i=1;i<=10;i++) cout<<d[i]<<endl;
    45     int t;
    46     cin>>t;
    47     while(t--){
    48         ll n,c;
    49         cin>>n>>c;
    50         cout<<quick(c,d[n])<<endl;
    51     }
    52     return 0;
    53 }
    View Code

    C. Count New String

    题意

    f(S,x,y)(1≤x≤y≤n)表示一个长度为y-x+1的字符串,字符串的第 i 位为maxi=x...x+k−1Si。 A={ f( f(S,x1,y1), x2−x1+1 , y2−x1+1 ) | 1≤x1≤x2≤y2≤y1≤n},求有多少不同的集合A。

    题解

    出题人的题解:

    • 核心点 1

    • 这题等价于 f(S,i,n) 这 n 个串的不同子串的个数

    • 核心点 2

    • 假设当前字符的位置是 i,最近的大于等于它的字符的位置
    是 j,那么新增的代价是 j-i。
    • f(S,i,n) 翻转后构成的字典树的大小不超过 10N

    • 所以我们要考虑这个字典树本质不同的子串

    • 最暴力的方法:在构成的字典树上建广义后缀自动机即可,
    设 k 为字符集大小,复杂度O(Nk2)
    • 也可以用一些哈希或序列自动机的方法求一求。
     
    我的想法:
    比赛的时候第一点我就没看出来,好菜.....
    A集合中区间范围可以看出[x2,y2]完全包含在[x1,y1]里边,所以[x2,y2]这个是不起任何作用的。故问题就转换成了求f(S,x,y)(1xyn)有多少本质不同的子串。这个问题不就是广义后缀自动机的经典题型了吗。但是如果对于所有的[x1,y1]都建立sam的话,肯定是不行的,于是就要用到别的办法。观察可以发现f(S,x,y)调用过程中任意一个位置的字符最多会被替换9次(因为子集字符串长度最大为10)。看巨巨们的博客有两种方法,一种用单调队列完成,一种是序列自动机。找到第一个大于等于s[i]的字符的位置记录为pos,那么只要每次往sam中插入s[i]~s[pos-1]即可。
     
    关于序列自动机的做法:(来自@Frozen_Guardian )nt[ i ][ j ] 表示为记录位置 i 开始的首次大于等于字母 j 的位置,这样对于第 i 个位置的字母 j 来说,只需要暴力更新一下 [ i , nt[ i ][ j ] - 1 ] 就好了,nt[ i ][ j ] 往后的位置都可以直接从前面维护的 trie 树中拉下来继续用。

    代码

    序列自动机+SAM
     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4  
     5 const int maxn=1e6+10;
     6  
     7 struct state
     8 {
     9     int mxlen,link;
    10     int cnt;
    11     int nt[26];
    12 }st[maxn<<1];
    13  
    14 int sz,last,len;
    15 char s[maxn];
    16  
    17 inline void sam_init()
    18 {
    19    st[0].mxlen=0;
    20    st[0].link=-1;
    21    sz=1;
    22    last=0;
    23 }
    24  
    25 inline void sam_extend(int c)
    26 {
    27     int cur=sz++;
    28     st[cur].mxlen=st[last].mxlen+1;
    29     st[cur].cnt=1;
    30     int p=last;
    31     while(p!=-1&&!st[p].nt[c]){
    32         st[p].nt[c]=cur;
    33         p=st[p].link;
    34     }
    35     if(p==-1) st[cur].link=0;
    36     else{
    37         int q=st[p].nt[c];
    38         if(st[p].mxlen+1==st[q].mxlen) st[cur].link=q;
    39         else{
    40             int clone=sz++;
    41             st[clone].mxlen=st[p].mxlen+1;
    42             memcpy(st[clone].nt,st[q].nt,sizeof(st[q].nt));
    43             st[clone].link=st[q].link;
    44             while(p!=-1&&st[p].nt[c]==q){
    45                 st[p].nt[c]=clone;
    46                 p=st[p].link;
    47             }
    48             st[q].link=st[cur].link=clone;
    49         }
    50     }
    51     last=cur;
    52 }
    53  
    54 int nt[maxn][10],id[maxn];
    55 //nx[i][j]第i个位置(包括)后首次出现大于等于j的位置 
    56  
    57 int main()
    58 {
    59     cin>>s+1;
    60     sam_init();
    61     int n=strlen(s+1);
    62     for(int i=0;i<10;i++) nt[n+1][i]=n+1;
    63     for(int i=n;i>=1;i--){
    64         for(int j=0;j<10;j++) nt[i][j]=nt[i+1][j];
    65         nt[i][s[i]-'a']=i;
    66         for(int j=8;j>=0;j--) nt[i][j]=min(nt[i][j],nt[i][j+1]);
    67     }
    68     id[n+1]=last;
    69     for(int i=n;i>=1;i--){
    70         int pos=nt[i+1][s[i]-'a'];
    71         last=id[pos];
    72         for(int j=i;j<pos;j++) sam_extend(s[i]-'a');
    73         id[i]=last;
    74     }
    75     ll ans=0;
    76     for(int i=1;i<=sz;i++){
    77         ans+=st[i].mxlen-st[st[i].link].mxlen;
    78     }
    79     cout<<ans<<endl;
    80     return 0;
    81 }
    View Code

    单调队列+SAM

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4  
     5 const int maxn=1e6+10;
     6  
     7 struct state
     8 {
     9     int mxlen,link;
    10     int cnt;
    11     int nt[26];
    12 }st[maxn<<1];
    13  
    14 int sz,last,len;
    15 char s[maxn];
    16  
    17 inline void sam_init()
    18 {
    19    st[0].mxlen=0;
    20    st[0].link=-1;
    21    sz=1;
    22    last=0;
    23 }
    24  
    25 inline void sam_extend(int c)
    26 {
    27     int cur=sz++;
    28     st[cur].mxlen=st[last].mxlen+1;
    29     st[cur].cnt=1;
    30     int p=last;
    31     while(p!=-1&&!st[p].nt[c]){
    32         st[p].nt[c]=cur;
    33         p=st[p].link;
    34     }
    35     if(p==-1) st[cur].link=0;
    36     else{
    37         int q=st[p].nt[c];
    38         if(st[p].mxlen+1==st[q].mxlen) st[cur].link=q;
    39         else{
    40             int clone=sz++;
    41             st[clone].mxlen=st[p].mxlen+1;
    42             memcpy(st[clone].nt,st[q].nt,sizeof(st[q].nt));
    43             st[clone].link=st[q].link;
    44             while(p!=-1&&st[p].nt[c]==q){
    45                 st[p].nt[c]=clone;
    46                 p=st[p].link;
    47             }
    48             st[q].link=st[cur].link=clone;
    49         }
    50     }
    51     last=cur;
    52 }
    53  
    54 int id[maxn];
    55  
    56 int main()
    57 {
    58     cin>>s+1;
    59     sam_init();
    60     int n=strlen(s+1);
    61     stack<int>ss;
    62     ss.push(n+1);
    63     id[n+1]=last;
    64     for(int i=n;i>=1;i--){
    65         while(ss.size()!=1&&s[ss.top()]<s[i]) ss.pop();
    66         int pos=ss.top();
    67         last=id[pos];
    68         for(int j=i;j<pos;j++) sam_extend(s[i]-'a');
    69         id[i]=last;
    70         ss.push(i);
    71     }
    72     ll ans=0;
    73     for(int i=1;i<=sz;i++){
    74         ans+=st[i].mxlen-st[st[i].link].mxlen;
    75     }
    76     cout<<ans<<endl;
    77     return 0;
    78 }
    View Code
     

    F. Finding the Order

    题意

    有两条平行的线AB和CD,给出AC, AD, BC, BD 的长度,分别为a, b, c, d。问是AB//CD,还是AB//DC。

    题解

    • 这是一个神奇的题目,我是把所有情况画出来记录,毕竟暴力出奇迹嘛^_^
    • 发现自己是憨憨,看了其他巨巨的题解发现自己的方法好蠢,我连小学数学的水平都没有┭┮﹏┭┮ ,这个解法来自@Harris-H。根据两边之和大于第三边,直接比较AC+BD和AB+BC的长度就好了,长的是四边形的交线,就可以判断是CD还是DC了。

    代码

    菜鸡本人的代码

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define pb push_back
     4 #define ft first
     5 #define sd second
     6 #define pii pair<int,int>
     7 #define pll pair<ll,ll>
     8 using namespace std;
     9  
    10 int main()
    11 {
    12     ios::sync_with_stdio(false);
    13     cin.tie(0);
    14     cout.tie(0);
    15     int t;
    16     cin>>t;
    17     while(t--){
    18         int a,b,c,d;
    19         cin>>a>>b>>c>>d;
    20         if(b>a&&c>d&&b>d||a>b&&c>d&&c>a||b>a&&d>c&&b>d||a==b&&c>d&&c>a||c==d&&b>a&&b>d) cout<<"AB//CD"<<endl;
    21         else cout<<"AB//DC"<<endl;
    22     }
    23     return 0;
    24 }
    View Code

    巨巨的代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
     5 #define mst(a) memset(a,0,sizeof a)
     6 #define lx x<<1
     7 #define rx x<<1|1
     8 #define reg register
     9 #define PII pair
    10 #define fi first
    11 #define se second
    12 #define pb push_back
    13 int main(){
    14     int t;
    15     cin>>t;
    16     while(t--){
    17         int a,b,c,d;
    18         cin>>a>>b>>c>>d;
    19         puts(b+c>a+d?"AB//CD":"AB//DC");
    20     }
    21     return 0;
    22 }
    View Code

    H. Haeder Gcd Problem

    题意

    集合A和B都是{1,2,.....,n}的子集,A∩B≠∅。问A和B最多有多少对数GCD(Ap,Bq)>1。

    题解

    所有gcd>1的两个数肯定是倍数关系,最小肯定是本身和2倍。越大的数能和他匹配的就会越少,为了简单,从大的数往小的数匹配。先用线性筛O(n)预处理出1-n之间的质数,然后找到小于n的质数最大,并且要有至少一个数可以和他配对。从这个质数开始往下计算,如果有偶数个可以和该质数配对的,那就直接记录下来;如果是奇数个,最简单的方法是将这个数的二倍放弃,剩下的记录下来。因为是2的倍数,肯定存在,并且方便计算。

    代码

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 #define pb push_back
     4 #define ft first
     5 #define sd second
     6 #define pii pair<int,int>
     7 #define pll pair<ll,ll>
     8 using namespace std;
     9  
    10 ll st[200100];
    11 ll prime[200100];
    12 ll cnt;
    13  
    14 void Prime(ll n)
    15 {
    16     cnt=0;
    17     for(ll i=2;i<=n;i++){
    18         if(!st[i]) prime[cnt++]=i;
    19         for(int j=0;prime[j]*i<=n;j++){
    20             st[prime[j]*i]=1;
    21             if(i%prime[j]==0) break;
    22         }
    23     }
    24 }
    25  
    26 ll  quick(int a,int b)
    27 {
    28     int res=1;
    29     while(b){
    30         if(b&1) res=(res*a);
    31         a=(a*a);
    32         b>>=1;
    33     }
    34     return res;
    35 }
    36  
    37 bool vis[200100];
    38 vector<int>v[200100];
    39  
    40 int main()
    41 {
    42     ios::sync_with_stdio(false);
    43     cin.tie(0);
    44     cout.tie(0);
    45     int t;
    46     cin>>t;
    47     Prime(200000);
    48     while(t--){
    49         memset(vis,0,sizeof(vis));
    50         int n;
    51         cin>>n;
    52         for(int i=0;i<=n;i++) v[i].clear();
    53         int k=cnt-1;
    54         for(int i=0;i<cnt;i++){
    55             if(prime[i]>n) {k=i-1;break;}
    56         }
    57         int ans=0;
    58         for(int i=k;i>=0;i--){
    59             int p=n/prime[i],x=0;
    60             for(int j=1;j<=n/prime[i];j++)
    61                 if(!vis[prime[i]*j]) x++;
    62             if(x&1){
    63                 int flag=0;
    64                 if(n/prime[i]<2) continue;
    65                 for(int j=1;j<=n/prime[i];j++){
    66                     if(!flag&&!vis[prime[i]*j]&&(prime[i]*j)%2==0) flag=1;     //删掉一个2的倍数
    67                     else if(!vis[prime[i]*j]) {
    68                         vis[prime[i]*j]=1;
    69                         v[i].push_back(prime[i]*j);
    70                     }
    71                 }
    72             }
    73             else{
    74                 for(int j=1;j<=n/prime[i];j++)
    75                     if(!vis[prime[i]*j]) {
    76                         vis[prime[i]*j]=1;
    77                         v[i].push_back(prime[i]*j);
    78                     }
    79             }
    80         }
    81         for(int i=0;i<=k;i++){
    82             ans+=v[i].size()/2;
    83         }
    84         cout<<ans<<endl;
    85         for(int i=0;i<=k;i++){
    86             for(int j=1;j<v[i].size();j+=2){
    87                 cout<<v[i][j-1]<<' '<<v[i][j]<<endl;
    88             }
    89         }
    90     }
    91     return 0;
    92 }
    View Code
  • 相关阅读:
    毕业了!
    mesos无执行器启动docker
    docker run option
    maintenance
    virtualenv
    multi role
    排序之插入排序
    DLU-1064 美声葱
    codeforces-1025 A Doggo Recoloring
    codeforces-1027 C Minimum Value Rectangle
  • 原文地址:https://www.cnblogs.com/lilibuxiangtle/p/13352686.html
Copyright © 2011-2022 走看看