• CSP-S 模拟测试57题解


    人生第一次A,B层一块考rank2,虽然说分差没几分,但还是值得纪念。

    题解:

    T1 天空龙:

    大神题,因为我从不写快读也没有写考场注释的习惯,所以不会做,全hzoi就kx会做,kx真大神级人物。

    T2 巨神兵:

    大神题,一看数据范围这么小,我们考虑状压,最傻逼的暴力思路是压边,但是这显然不行。正解是压点,设$f[s]$为当前选定点集状态为$s$的方案数。

    我们考虑转移,当前选定的点集肯定是可以通过边和没有连过来的点相连构成新的方案。所以转移所以我们考虑枚举补集的子集$k$,设$cnt$为s与k两个点集相连的边数,那么转移为$f[s|k]+=f[s]*2^{cnt}$?不,这样会算重,因为比如我们先加A点,再加B点,和先加B点,再加A点是一样的。所以考虑容斥,容斥系数为$(-1)^{size[k]+1}$,蒟蒻博主不会正着推,但是这个可以证明是对的。我们设$g[i]$为转移了$i$层的系数,那么显然$g[0]=1$,然后$g$的递推式为$g[i]=sum_{j=1}^{i}{C_{j}^{i}*(-1)^{j+1}*g[i-j]}$,我们要证的是$g[i]=1$,首先$g[i-j]$可以消掉,因为你由3层转移到5层,和从0层转移到2层是一样的然后

        $sum_{j=1}^{i}{C_j^i*(-1)^{j+1}}$

        $=(-1)*(sum_{j=0}^{i}{C_j^i*(-1)^{j}}-C_j^0*(-1)^0)$

        $=(-1)*((1-1)^i-1)=1$    证毕。

    然后转移用了很多预处理,主要是找两个点集相连的边,我们首先预处理每个点与之相连点的状态,将它与上当前枚举的补集就好,然后还要预处理的是,每个二进制数中1的个数和每个取出来的1,是第几位,然后dp就好了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define int long long
     4 const int N=1900000,mod=1e9+7;
     5 int first[N],nex[N],to[N],tot,rc[N],sta[N],ma[5242880],f[5242880],n,m,qpow[N],re[N];
     6 void add(int a,int b){
     7     to[++tot]=b,nex[tot]=first[a],first[a]=tot;
     8 }
     9 signed main(){
    10     scanf("%lld%lld",&n,&m);
    11     for(int i=1;i<=m;++i){
    12         int x,y;
    13         scanf("%lld%lld",&x,&y);
    14         add(x,y);
    15     }
    16     for(int i=0;i<=(1<<n);++i){
    17         ma[i]=ma[i>>1]+(i&1);
    18     }
    19 //    for(int i=1;i<=1<<n;++i) cout<<"ma["<<i<<"]=="<<ma[i]<<" ";cout<<endl;
    20     f[0]=qpow[0]=1;
    21     for(int i=1;i<=n*n;++i) qpow[i]=(qpow[i-1]<<1)%mod;
    22     for(int i=0;i<=n+1;++i) rc[i]=(i&1)?-1:1;
    23 //    for(int i=0;i<=n+1;++i) cout<<rc[i]<<" ";cout<<endl;
    24     for(int i=1;i<=n;++i){
    25         re[1<<i-1]=i;
    26         for(int j=first[i];j;j=nex[j]){
    27             sta[i]|=1<<to[j]-1;
    28         }
    29     }
    30     int Max=1<<n,cnt=0,maxn=Max-1;
    31     for(register int s=0;s^Max;++s){register int kl=~s;
    32         for(register int i=kl&maxn;i;i=(i-1)&kl){
    33             cnt=0;
    34             for(register int j=s;j;j-=j&-j) cnt+=ma[sta[re[j&-j]]&i];
    35 //            cout<<cnt<<" ";
    36 //            cout<<f[s]%mod*qpow[cnt]%mod<<" ";
    37             (f[s|i]+=rc[ma[i]+1]*f[s]%mod*qpow[cnt]%mod)%=mod;
    38         }
    39     }
    40     printf("%lld
    ",(f[maxn]+mod)%mod);
    41 }
    obelisk

     T3 太阳神:

    正难则反,lcm大于n的不好求,我们可以考虑求小于n的,即$n^2-sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}{[lcm(i,j)<=n]}$

    然后再转化$n^2-sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}{[frac{i imes j}{gcd(i,j)}<=n]}$,难点在于求后面的那部分,现在我们考虑枚举,$i,j$的最大公约数d,柿子就变成了$sumlimits_{d=1}^{n}sumlimits_{a=1}^{frac{n}{d}}sumlimits_{b=1}^{frac{n}{d}}{[gcd(a,b)==1][a imes bleq frac{n}{d}]}$

    我们考虑求$f_k=sumlimits_{1leq a,bleq n}{[a imes bleq k][gcd(a,b)==1]}$,我们设$S_k=sumlimits_{1leq a,bleq n}{[a imes bleq k]}$,这一个数论分块就可以求出,那么$f_k=S_k-sumlimits_{t>1} f_{frac{k}{t^2}}$,前面S函数是不考虑gcd为1的条件的,后面的t相当于是枚举a,b的因子,同时把a,b因子提出即可得到$frac{k}{t^2}$,然后就是关于f函数的求法,把它差分一下得到g[k],那么g[k]的含义就是$sumlimits [a imes b=k][gcd(a,b)=1]$的a,b对数

    $g[k]=2^cnt$,cnt为k的质因子种数,因为每种质因子,只能全部给a或b,而不能一部分给a,一部分给b,因为那样的话$gcd(a,b)$就不是1,这样f线筛即可,当n较小时直接用线筛筛出来的,当n较大时用上面提到的方法迭代即可。最外层数论分块,时间复杂度O(玄学)$O(n^{frac{2}{3}})$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define int long long
     4 const int N=1e7+10,mod=1e9+7;
     5 int v[N],prime[N],num,f[N],n;
     6 void init(){
     7     f[1]=1;
     8     for(int i=2;i<=10000000;++i){
     9         if(!v[i]){
    10             prime[++num]=i;
    11             f[i]=2;
    12             v[i]=1;
    13         }
    14         for(int j=1;j<=num&&i*prime[j]<=10000000;++j){
    15             v[i*prime[j]]=1;
    16             if(i%prime[j]==0){
    17                 f[i*prime[j]]=f[i];
    18                 break;
    19             }
    20             f[i*prime[j]]=f[i]*f[prime[j]]%mod;
    21 //            cout<<f[i]<<" "<<f[prime[j]]<<" "<<f[i*prime[j]]<<endl;
    22         }
    23     }
    24 }
    25 int calS(int x){
    26     int ans=0;
    27     for(int l=1,r;l<=x;l=r+1){
    28         r=x/(x/l);
    29         (ans+=(r-l+1)*(x/l)%mod)%=mod;
    30     }
    31     return ans;
    32 }
    33 
    34 int calF(int x){//cout<<x<<" "<<endl;
    35     if(x<=10000000) return f[x];
    36     int ans=0;
    37     ans=calS(x)%mod;
    38     for(int i=2;i*i<=x;++i){
    39         (((ans-=calF(x/(i*i))+mod)%=mod)+=mod)%=mod;
    40     }
    41     return ans;
    42 }
    43 
    44 signed main(){
    45     scanf("%lld",&n);
    46     init();
    47     //for(int i=1;i<=20;++i) cout<<"f["<<i<<"]=="<<f[i]<<" ";cout<<endl;
    48     for(int i=1;i<=10000000;++i) (f[i]+=f[i-1])%=mod;
    49     int ans=(n%mod*(n%mod))%mod;
    50     for(int i=1,r;i<=n;i=r+1){
    51         r=n/(n/i);
    52 //        cout<<ans<<" ";
    53 //        cout<<i<<" "<<r<<endl;
    54         (((ans-=calF(n/i)%mod*(r-i+1)%mod+mod)%=mod)+=mod)%=mod;
    55 //        cout<<calF(n/i)%mod*(r-i+1)%mod+mod<<endl;
    56     }
    57     printf("%lld",(ans+mod)%mod);
    58 }
    ra
  • 相关阅读:
    ubuntu部分端口命令的使用----开启端口/开启防火墙
    ElasticSearch 5.0及head插件安装
    维基百科语料中的词语相似度探索
    Mac下多版本JDK安装
    Mac OS 终端利器 iTerm2
    android mat 转 bitmap
    simHash 简介以及 java 实现
    Python数据可视化之matplotlib实践 源码 第二篇 精进 第六章
    Python数据可视化之matplotlib实践 源码 第二篇 精进 第五章
    Python数据可视化之matplotlib实践 源码 第一篇 入门 第四章
  • 原文地址:https://www.cnblogs.com/leom10/p/11623873.html
Copyright © 2020-2023  润新知