[考试反思]0420省选模拟75:安在

好像并没有什么突出的表现,但是不知道为什么名次居然苟上来了。

可能是昨天放假今天大家的状态比较差(被动技能:熬夜引起的状态削减效果降低$65\%$)

$T1$是一个貌似比较靠直觉的题,顺着第一反应想到了。

$T2$推式子只想到一半,然后把$n^2log$优化成$O(n)$多拿$30$然而特判写错又挂掉$10$分。

$T3$剩的时间不够就没弄。

大体还行吧。困死了。

T1:比特币

大意:维护多重集支持插入,删除所有等于$x$的值,所有数$+x$,查询集合中有多少二进制第$k$位是$1$的数。$k <18,nle 5 imes 10^5,x le 10^9$

一看到$x$这么大$k$这么小,第一反应是取模,多出的位显然没用。

发现答案只与$k$有关且$k$很小所以我们对于每个$k$维护答案。

直接弄$k$个树状数组,每个是模$2^{i+1}$意义下的,要查询$[2^i,2^{i+1})$的权值和。

所有数加的话只需要全局弄一个标记。多重清空只要用$map$存一下每个原数出现次数。

我弱智写了个线段树。写的又长跑得又慢。时间复杂度$O(nk^2)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 map<ll,int>M;
 5 struct Segment_Tree{
 6     int w[1<<20],B;
 7     #define lc p<<1
 8     #define rc lc|1
 9     #define md (L+R>>1)
10     void add(int x,int v,int p,int L,int R){
11         w[p]+=v; if(L==R)return;
12         if(x<=md)add(x,v,lc,L,md);else add(x,v,rc,md+1,R);
13     }
14     int ask(int l,int r,int p,int L,int R){
15         if(l<=L&&R<=r)return w[p];
16         return (l<=md?ask(l,r,lc,L,md):0)+(r>md?ask(l,r,rc,md+1,R):0);
17     }
18     int ask(int l,int r){return l<=r?ask(l,r,1,0,B):ask(l,B,1,0,B)+ask(0,r,1,0,B);}
19     void add(int x,int v){add(x,v,1,0,B);}
20 }T[18];
21 int mo(ll x,int b){b=1<<b+1;return (x%b+b)%b;}
22 int main(){
23     for(int i=0;i<=17;++i)T[i].B=(1<<i+1)-1;
24     int n,op;ll x,tag=0;
25     cin>>n;
26     while(n--){
27         scanf("%d%lld",&op,&x);
28         if(op==0){
29             x-=tag;M[x]++;
30             for(int i=0;i<=17;++i)T[i].add(mo(x,i),1);
31         }else if(op==1){
32             x-=tag;int t=M[x];M[x]=0;
33             for(int i=0;i<=17;++i)T[i].add(mo(x,i),-t);
34         }else if(op==2)tag+=x;
35         else printf("%d
",T[x].ask(mo((1<<x)-tag,x),mo((1<<x+1)-1-tag,x)));
36     }
37 }
View Code

T2:测试

大意:$sumlimits_{i=1}^{A}sumlimits_{j=1}^{B}sumlimits_{k=1}^{C}d(ijk),A,B,C le 10^5$

经久不衰的结论:$d(nm)=sumlimits_{i|n}sumlimits_{j|m}[(i,j)=1]$

含义理解的话,就是考虑所有因子的分配。因为要求$ij$互质,所以说因子一定只分配在一边。

我们认定,如果对于质因子$p$,$n,m,i,j$中分别有$x,y,a,b$个。

那么如果$a=0,b eq 0$则我们认为这对$(i,j)$对应着一个有$p^{x+b}$的数。否则对应$p^{a}$

这样所有因数都与一个$(i,j)$对应了。

这个结论可以扩展到$3$个数,也即$d(xyz)=sumlimits_{i|x}sumlimits_{j|y}sumlimits_{k|z} [(i,j)=1][(i,k)=1][(j,k)=1]$

那么原问题经过一些简单的操作就会变为:$ans=sumlimits_{i=1}^{A} frac{A}{i} sumlimits_{j=1}^{B} frac{B}{j} sumlimits_{k=1}^{C} frac{C}{k} [(i,j)=1][(i,k)=1][(j,k)=1]$

莫反一下就是$sumlimits_{d=1} mu(d) sumlimits_{z=1}^{C} frac{C}{z} [(d,z)=1] sumlimits_{x=1}^{frac{A}{d}} frac{A}{dx} [(x,z)=1] sumlimits_{y=1}^{frac{B}{d}} frac{B}{dy} [(y,z)=1]$

递推处理$gcd$关系,这个式子暴力做能做到$O(n^2)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 8001
 4 #define ui unsigned int
 5 bool g[S][S],np[S];int p[S],pc,mu[100005],A,B,C,oA[S],oB[S],cA,cB,rA[188],rB[188];
 6 ui G[100005],fA[S][188],fB[S][188],ans;
 7 vector<int>D[S];
 8 int main(){
 9     cin>>A>>B>>C;
10     mu[1]=1;
11     for(int i=2;i<S;++i){
12         if(!np[i])p[++pc]=i,mu[i]=-1;
13         for(int j=1;i*p[j]<S;++j)
14             if(i%p[j])np[i*p[j]]=1,mu[i*p[j]]=-mu[i];
15             else{np[i*p[j]]=1;break;}
16     }
17     for(int i=1;i<S;++i)for(int j=i;j<S;j+=i)D[j].push_back(i);
18     for(int i=1;i<S;++i)for(int j=1,I,l;I=i/j,j<=i;j=l+1)l=i/I,G[i]+=(l-j+1)*I; 
19     for(int i=A;i;--i)if(!oA[A/i])rA[oA[A/i]=++cA]=A/i;
20     for(int i=B;i;--i)if(!oB[B/i])rB[oB[B/i]=++cB]=B/i;
21     for(int i=2;i<=A&&i<=B;++i){
22         if(!np[i])for(int j=i;j<=C;j+=i)g[i][j]=1;
23         else{int x=D[i][1],y=i/x;for(int j=2;j<=C;++j)g[i][j]=g[x][j]|g[y][j];}
24     }
25     for(int z=1;z<=C;++z)for(int i=1;i<=cA;++i)for(int j=0;j<D[z].size();++j)fA[z][i]+=mu[D[z][j]]*G[rA[i]/D[z][j]];
26     for(int z=1;z<=C;++z)for(int i=1;i<=cB;++i)for(int j=0;j<D[z].size();++j)fB[z][i]+=mu[D[z][j]]*G[rB[i]/D[z][j]];
27     for(int d=1,a,b;a=oA[A/d],b=oB[B/d],d<=A&&d<=B;++d)if(mu[d])for(int z=1;z<=C;++z)if(!g[d][z])ans+=fA[z][a]*fB[z][b]*mu[d]*(C/z);
28     cout<<ans<<endl;
29 }
View Code

我们设$f_x(n)=sumlimits_{i=1}^{n} [(i,x)=1] mu(i),g_x(n)=sumlimits_{i=1}^{n} [(i,x)=1] frac{n}{i}$

则原式为$ans=sumlimits_{x=1}^{A} frac{A}{x} sumlimits_{d=1}^{B} [(x,d)=1] mu(d) g_x(frac{B}{d}) g_x(frac{C}{d})$

枚举$x$,将$d$整除分块,用定义的$f$前缀和做差求区间和,如果后面能够在合法复杂度内预处理得到那么总复杂度就是$O(nsqrt{n})$

考虑如何预处理$f,g$:首先不难发现对于所有的$x$,我们把它的所有出现次数超过$1$的质因子次数都调整为$1$,$f,g$均不变

那么只需要考虑每种质因子都只包含一个的情况。对于一个数$x$设其一个质因子为$p$

因为我们知道不含平方因子后$(frac{p}{x},x)=1$,所以我们直接考虑去掉质因子的所有倍数的贡献即可

列式化简可以得到$f_x(n)=f_{frac{x}{p}}(n) + f_x(frac{n}{p}),g_x(n)=g_{frac{x}{p}}(n) - g_{frac{x}{p}}(frac{n}{p}) $

然后我们可以简单的预处理$f_1(n),g_1(n)$

且上面涉及到的所有的$n$都是整除分块时产生的数,总共有$O(sqrt{n})$个。

所以只要最开始把所有整除分块涉及的值提出来对这些数做上面的$dp$。

然而存不下,所以我们采用搜索,每次添加一个质因数那样。

总复杂度就控制在了$O(nsqrt{n})$。常数有点大。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ui unsigned int
 4 #define S 100001
 5 int p[S],pc,np[S],lp[S],ep[S],mu[S],_[S],c,v[S],A,B,C;
 6 ui sum[S],tot[S],ans,F[33][2333],G[33][2333];
 7 vector<int>pr;
 8 void sch(int x,int Lp,int d){
 9     for(int i=1,r;i<=B;i=r+1)r=min(B/(B/i),C/(C/i)),ans+=tot[x]*(F[d][_[r]]-F[d][_[i-1]])*G[d][_[B/i]]*G[d][_[C/i]];
10     for(int i=Lp,P;x*1ll*(P=pr[i])<=A;++i){
11         for(int j=1,n;n=v[j];++j)F[d+1][j]=F[d][j]+F[d+1][_[n/P]],G[d+1][j]=G[d][j]-G[d][_[n/P]];
12         sch(x*P,i+1,d+1);
13     }
14 }
15 int main(){
16     cin>>A>>B>>C; if(A>C)swap(A,C); if(A>B)swap(A,B); if(B>C)swap(B,C);
17     for(int i=2;i<=C;++i){
18         if(!np[i])p[++pc]=i,lp[i]=ep[i]=i,mu[i]=-1,pr.push_back(i);
19         for(int j=1,x;(x=i*p[j])<=C;++j)
20             if(i%p[j])lp[x]=p[j],ep[x]=ep[i]*p[j],mu[x]=-mu[i],np[x]=1;
21             else {lp[x]=p[j];np[x]=1;ep[x]=ep[i];break;}
22     }pr.push_back(C+1);
23     for(int i=lp[1]=ep[1]=mu[1]=1;i<=C;++i)mu[i]+=mu[i-1];
24     for(int x=1;x<=A;++x)tot[ep[x]]+=A/x;
25     for(int i=1,r,N;N=B/i,i<=B;i=r+1)r=B/N,v[++c]=N;
26     for(int i=1,r,N;N=C/i,i<=C;i=r+1)r=C/N,v[++c]=N;
27     sort(v+1,v+1+c); c=unique(v+1,v+1+c)-v-1;
28     for(int i=1;i<=c;++i){
29         _[v[i]]=i,F[0][i]=mu[v[i]],G[0][i]=sum[v[i]];
30         for(int j=1,N,r;N=v[i]/j,j<=v[i];j=r+1)r=v[i]/N,G[0][i]+=N*(r-j+1);
31     }sch(1,0,0); cout<<ans<<endl;
32 }
View Code

好像只有我是写的题解做法。$LNC$的神奇做法常数更小一些可以去看,懒得套娃了。

https://www.cnblogs.com/Lrefrain/p/12738546.html

被大神谴责了,娃是必须套的。

T3:光图

大意:求$n$点所有带标号无向图的联通块数的$k$次方之和。多测。$T le 10^5,n le 5 imes 10^4,k le 15$

前置:fft1专题《城市规划》。(这次写的是一个$log$的多项式求逆做法,可以再回去翻$LNC$大神的博客)

求出所有带标号联通图个数之后。考虑这道题。

假如联通块数是$T$则$T^k=(1+1+1+...+1)^k$。也就是说,从$T$个$1$中随便可重复的选择$k$次的方案数。

枚举我们一共选到了几种。$ans=sumlimits_{i=1}^{k} egin{Bmatrix} k \ i end{Bmatrix} i! inom{T}{i}$

把$T$拿出来,如此考虑含义:我们枚举一个由$i$个联通块构成的集合。我们考虑有多少个图里包含了这样的集合。

首先加入我们能求出$g_i(n)$表示$i$个联通块$n$个点的图的个数。

那么$ans=sumlimits_{i=1}^{k} egin{Bmatrix} k \ i end{Bmatrix} i! sumlimits_{j=1}^{n} g_i(j) 2^{inom{n-j}{2}} inom{n}{j}$

也就是选出若干点构成特定形状作为枚举的联通块集合,剩下的部分随意连边。

这样可以$O(k)$回答单次询问。

问题只剩下如何求出$g$。首先$g_1$就是《城市规划》

$g_t(x) = g_1(i) g_{t-1}(x-i) inom{x-1}{i-1}$

组合数是一个钦定。

总的时间复杂度是$O(knlogn)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1004535809
 4 #define S 262144
 5 int rev[S],r[S],n,A[S],B[S],fac[S],inv[S],G0[S],G1[S],F[16][S],FR[S],s[16][16],E[16][S],len,pw[S];
 6 int mo(int x){return x>=mod?x-mod:x;}
 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 8 void NTT(int*a,int op=1){
 9     for(int i=1;i<len;++i)if(rev[i]>i)swap(a[i],a[rev[i]]);
10     for(int i=1;i<len;i<<=1)for(int j=0,w=qp(3,(mod-1)/2/i*op+mod-1);j<len;j+=i<<1)
11         for(int k=j,x,y,t=1;k<j+i;++k,t=1ll*t*w%mod)
12             x=a[k],y=a[k+i]*1ll*t%mod,a[k]=mo(x+y),a[k+i]=mo(x-y+mod);
13     if(op-1)for(int iv=qp(len,mod-2),i=0;i<len;++i)a[i]=1ll*a[i]*iv%mod;
14 }
15 void sat(int n){
16     len=1; while(len<=n)len<<=1;
17     for(int i=1;i<len;++i)rev[i]=rev[i>>1]>>1|(i&1?len>>1:0);
18 }
19 void Inv(int*a,int n){
20     if(n==1){r[0]=qp(a[0],mod-2);return;}
21     Inv(a,n+1>>1); sat(n<<1);
22     for(int i=0;i<len;++i)A[i]=i<n?a[i]:0;
23     for(int i=0;i<len;++i)B[i]=r[i];
24     NTT(A);NTT(B);
25     for(int i=0;i<len;++i)r[i]=(B[i]+B[i]-1ll*B[i]*A[i]%mod*B[i]%mod+mod)%mod;
26     NTT(r,-1);
27     for(int i=n+1;i<len;++i)r[i]=0;
28 }
29 int main(){
30     for(int i=0;i<=50000;++i)pw[i]=qp(2,i*(i-1ll)/2);
31     s[0][0]=1;
32     for(int i=1;i<=15;++i)for(int j=1;j<=i;++j)s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*j)%mod;
33     for(int i=fac[0]=1;i<=50000;++i)fac[i]=fac[i-1]*1ll*i%mod;
34     inv[50000]=qp(fac[50000],mod-2);
35     for(int i=49999;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod;
36     for(int i=0;i<=50000;++i)G1[i]=pw[i]*1ll*inv[i]%mod,G0[i]=pw[i]*1ll*(i?inv[i-1]:0)%mod;
37     Inv(G1,50001);sat(100000); NTT(r); NTT(G0);
38     for(int i=0;i<len;++i)FR[i]=1ll*G0[i]*r[i]%mod; NTT(FR,-1); FR[0]=0;
39     for(int i=0;i<len;++i)F[1][i]=1ll*FR[i]*fac[i-1]%mod;
40     for(int i=50001;i<len;++i)FR[i]=0;
41     NTT(FR);
42     for(int t=2;t<=15;++t){
43         for(int i=0;i<len;++i)A[i]=0;
44         for(int i=1;i<=50000;++i)A[i]=F[t-1][i]*1ll*inv[i]%mod;
45         NTT(A); for(int i=0;i<len;++i)A[i]=1ll*A[i]*FR[i]%mod; NTT(A,-1);
46         for(int i=1;i<=50000;++i)F[t][i]=A[i]*1ll*fac[i-1]%mod;
47     }
48     for(int t=1;t<=15;++t){
49         for(int i=0;i<len;++i)A[i]=B[i]=0;
50         for(int i=0;i<=50000;++i)A[i]=pw[i]*1ll*inv[i]%mod,B[i]=F[t][i]*1ll*inv[i]%mod;
51         NTT(A);  NTT(B); for(int i=0;i<len;++i)E[t][i]=A[i]*1ll*B[i]%mod; NTT(E[t],-1);
52         for(int i=0;i<len;++i)E[t][i]=1ll*E[t][i]*fac[i]%mod;
53     }
54     int t,n,k,ans;cin>>t;while(t--){
55         ans=0;scanf("%d%d",&n,&k);
56         if(!k){puts("1");continue;}
57         for(int i=1;i<=k;++i)ans=(ans+1ll*s[k][i]*fac[i]%mod*E[i][n])%mod;
58         printf("%lld
",ans*1ll*qp(pw[n],mod-2)%mod);
59     }
60 }
View Code
原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12741272.html