CSP-S 模拟测试57题解

人生第一次A,B层一块考rank2,虽说分差没几分,但仍是值得记念。c++

题解:

T1 天空龙:ide

大神题,由于我从不写快读也没有写考场注释的习惯,因此不会作,全hzoi就kx会作,kx真大神级人物。函数

T2 巨神兵:spa

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

咱们考虑转移,当前选定的点集确定是能够经过边和没有连过来的点相连构成新的方案。因此转移因此咱们考虑枚举补集的子集$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层是同样的而后blog

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

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

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

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

 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\n",(f[maxn]+mod)%mod);
41 }
obelisk

 T3 太阳神:

正难则反,lcm大于n的很差求,咱们能够考虑求小于n的,即$n^2-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{[lcm(i,j)<=n]}$

而后再转化$n^2-\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}{[\frac{i\times j}{gcd(i,j)}<=n]}$,难点在于求后面的那部分,如今咱们考虑枚举,$i,j$的最大公约数d,柿子就变成了$\sum\limits_{d=1}^{n}\sum\limits_{a=1}^{\frac{n}{d}}\sum\limits_{b=1}^{\frac{n}{d}}{[gcd(a,b)==1][a\times b\leq \frac{n}{d}]}$

咱们考虑求$f_k=\sum\limits_{1\leq a,b\leq n}{[a\times b\leq k][gcd(a,b)==1]}$,咱们设$S_k=\sum\limits_{1\leq a,b\leq n}{[a\times b\leq k]}$,这一个数论分块就能够求出,那么$f_k=S_k-\sum\limits_{t>1} f_{\frac{k}{t^2}}$,前面S函数是不考虑gcd为1的条件的,后面的t至关因而枚举a,b的因子,同时把a,b因子提出便可获得$\frac{k}{t^2}$,而后就是关于f函数的求法,把它差分一下获得g[k],那么g[k]的含义就是$\sum\limits [a\times 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
相关文章
相关标签/搜索