[考试反思]0202省选模拟16:契机
100+0+0=100
后两题均没有提交。
今天题难度比昨天高不少。但是前两题还是可以想的。
T1和原题很像,45分钟抢了个绿框。
然后一看T2暴力10分T3暴力5分。
太少了不想写。不如集中时间想正解。
最后差不多是yy出了T2的正解,大体思路都是对的,但是只剩下26分钟,写不完了。
T3的话整个想偏了,也就没啥好说的了。
改题:
T2把我想的实现了之后发现它TLE35了。有一个细节没有注意导致被菊花图卡掉,下述。
然后就一直在跟T3的题解刚。题解没有解释也没有证明直接丢式子。
尝试从含义上理解行不通,尝试归纳证明也证不出来。
NC考场上打表找到了式子但是也不会证明,最后到了晚上挺晚的教练才说不用证了直接抄式子写代码。
然而发现剩下的地方也不容易,在最后一步突然发现了一个小问题然后又被卡住了,去问3个已经AC的人,两个不会一个不理我。
然后就到写总结的时间了。被一个垃圾题解吃了一下午时间。。。
T1:GCD与LCM
大意:给定多组n,m,a,求$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} [gcd(i,j) \le a] lcm(i,j) $。$T \le 10000,n,m,a\le 10^5$
和《数表》完全一个套路,式子稍有一点变化,剩下完全一样。不多说。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mod 1000000007 4 int t[111111],ans[111111],f[111111],mu[111111],Q,p[111111],pc,np[111111]; 5 int mo(int a){return a>=mod?a-mod:a;} 6 int ask(int p,int a=0){for(;p;p^=p&-p)a=mo(a+t[p]);return a;} 7 void add(int p,int w){for(;p<=100000;p+=p&-p)t[p]=mo(t[p]+w);} 8 struct qs{int n,m,a,o;friend bool operator<(qs x,qs y){return x.a<y.a;}}q[11111]; 9 main(){ 10 mu[1]=1; 11 for(int i=2;i<=100000;++i){ 12 if(!np[i])mu[p[++pc]=i]=-1; 13 for(int j=1,x;j<=pc&&(x=i*p[j])<=100000;++j) 14 if(i%p[j])mu[x]=-mu[i],np[x]=1; 15 else {np[x]=1;break;} 16 } 17 for(int i=1;i<=100000;++i)f[i]=mo(f[i-1]+i),mu[i]=mo(1ll*mu[i]*i*i%mod+mod); 18 cin>>Q;for(int i=1;i<=Q;++i)scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a),q[i].o=i; 19 sort(q+1,q+1+Q); 20 for(int z=1;z<=Q;++z){ 21 int n=q[z].n,m=q[z].m; 22 for(int i=q[z-1].a+1;i<=q[z].a;++i)for(int j=i;j<=100000;j+=i)if(mu[j/i])add(j,1ll*mu[j/i]*i%mod); 23 if(n>m)swap(n,m); 24 for(int l,i=1,N,M;N=n/i,M=m/i,i<=n;i=l+1)l=min(n/N,m/M),ans[q[z].o]=(ans[q[z].o]+1ll*f[N]*f[M]%mod*(ask(l)-ask(i-1)+mod))%mod; 25 }for(int i=1;i<=Q;++i)printf("%d\n",ans[i]); 26 }
T2:平面图
大意:平面图,强制在线,断边,询问两点是否联通,剩余联通块数。$n \le 100000,m \le 200000$
平面图当然是用来转对偶图的。
可以发现如果一条边两侧在对偶图中已经联通,那么断开这条边原图联通块数+1。
维护两点联通性,可拆分并查集?其实也就是暴力维护每个点属于那个联通块。
有什么能使复杂度对一点?想没路径压缩的并查集就是可撤销的,用到了啥?启发式合并。
所以你可以yy出一个启发式分裂。对于分裂出的较小的那一块我们暴力修改所有节点的所属联通块编号。
怎么判断两边哪个更大?bfs。
每次向两边都扩展一个点,这样总的扩展点数就是较小的一个的大小了,符合启发式的形式
然而没有完。这里”扩展一个点“的定义必须要明确。其实应该是队尾对齐而不是对首对其。
否则对于一个菊花,它被扩展多次,而度数很大还每次都扫全,肯定会TLE的。
所以开一个unordered_map/set记录边,及时删除,记录上一次扫到哪里了。这样复杂度就对了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1111111 4 unordered_map<int,int>M; unordered_set<int>E[S]; vector<int>v[S]; char o[3]; 5 int n,q,ec,f[S],bl[S],bc,la,fir[S],l[S],to[S],q1[S],q2[S],iq[S],t; 6 void link(int a,int b){l[ec]=fir[a];fir[a]=ec;to[ec]=b;} 7 void dfs(int p){bl[p]=bc;for(int i=fir[p];i;i=l[i])if(!bl[to[i]])dfs(to[i]);} 8 int find(int p){return f[p]==p?p:f[p]=find(f[p]);} 9 int F(int x,int y){return M[x*n+y];} 10 int main(){//freopen("1.in","r",stdin); 11 scanf("%d%d",&n,&q); 12 for(int i=1,k,a;i<=n;++i){ 13 scanf("%d",&k); 14 while(k-->0)scanf("%d",&a),v[i].push_back(a),M[i*n+a]=++ec,E[i].insert(a),link(i,a); 15 } 16 for(int i=1;i<=ec;++i)f[i]=i; 17 for(int i=1;i<=n;++i)for(int j=0;j<v[i].size();++j)f[find(F(i,v[i][(j+1)%v[i].size()]))]=find(F(v[i][j],i)); 18 for(int i=1;i<=n;++i)if(!bl[i])bc++,dfs(i); 19 while(q-->0){ 20 int x,y;scanf("%s%d%d",o,&x,&y);x^=la;y^=la; 21 if(o[0]=='-'){ 22 E[x].erase(y);E[y].erase(x); 23 if(find(F(x,y))==find(F(y,x))){ 24 int h1=0,h2=0,t1=1,t2=1;q1[1]=x;q2[1]=y;iq[x]=iq[y]=++t; 25 auto it1=E[x].begin(),it2=E[y].begin(); 26 while(h1<=t1&&h2<=t2){ 27 while(h1<=t1){ 28 if(it1==E[q1[h1]].end())h1++,it1=E[q1[h1]].begin(); 29 else if(iq[*it1]==t)it1++; 30 else{iq[q1[++t1]=*it1]=t;it1++;break;} 31 } 32 while(h2<=t2){ 33 if(it2==E[q2[h2]].end())h2++,it2=E[q2[h2]].begin(); 34 else if(iq[*it2]==t)it2++; 35 else{iq[q2[++t2]=*it2]=t;it2++;break;} 36 } 37 }++bc; 38 if(h1>t1)for(int i=1;i<h1;++i)bl[q1[i]]=bc; 39 else for(int i=1;i<h2;++i)bl[q2[i]]=bc; 40 }else f[f[F(x,y)]]=f[F(y,x)]; 41 printf("%d\n",la=bc); 42 }else printf("%d\n",la=bl[x]==bl[y]); 43 } 44 }
T3:路径
大意:从$(0,0)$走到$(n,m)$,设与$x=0,y=m$围成的面积为$a$则贡献$q^a$。答案对$p$取模。多测,同一测试点$p,q$相同。
$n,m,q \le 10^9,p \le 200000,T \le 200000$.$p,q$是质数
满的暴力分是矩阵快速幂优化dp。没细想。
正解是个没人会证只能打表的结论,设$f(n)=\prod\limits_{i=1}^{n} (q^i -1)$,则有$ans=\frac{f(n+m)}{f(n)f(m)}$
我们找到一个$k$使之满足$q^k \equiv 1 (mod \ p)$
然后我们就要求解$F$。但因为最后的答案有除法,所以我们要用类似$exLucas$的思路记录$p$的次数看上下是否相消。
考虑求$f$的$p$的次数以及提出所有$p$之后的剩下的系数。
$f(n)=\prod\limits_{i=1}^{n} (q^i-1)$
这样可以分$i$是否为$k$的倍数来讨论。不能整除的可以预处理。
能整除的部分形似$\prod\limits_{i=1}^{\frac{n}{k}} (q^{ik} -1)=(q^k -1)^{\frac{n}{k}} \prod\limits_{i=1}^{\frac{n}{k}} \sum\limits_{j=0}^{i-1} (q^i -1)$
(坑)
这个东西没有为什么。它很对就是了。后面的式子用等比数列求和公式可以得到前面的。
逆向套用等比数列求和公式这个思路很神奇啊。
现在我们的问题形式是$\prod\limits_{i=1}^{x} \sum\limits_{j=0}^{i-1} (q^j -1)$
还是分$i$能否被$p$整除来讨论。如果不能整除那么$\sum$的值就是$i$,乘起来就是阶乘。否则就是$p$的倍数。
也即要求$\prod\limits_{i=1}^{\frac{x}{p}} \sum\limits_{j=0}^{ip-1} (q^j -1) = ()^{\frac{x}{p}} \prod\limits_{i=1}^{\frac{n}{p}} \sum\limits_{j=0}^{i-1} (q^{jp} -1)$
还是等比数列求和,递归处理即可。
但是有个问题,我留坑的那里,那个$(q^k-1)^{\frac{n}{k]}$在去掉$p$之后剩余的系数如何计算?
实测当成1就行。因为最终你的答案是$f(n+m)/f(n)/f(m)$。
如果$n+m$在$k$进制下没有进位那么上下系数相消,如果进位了那么上面的$p$的次数就比下面多了你会输出0。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int p,q,n,m,t,k=1,tms,fac[222222],pr[222222],ans; 4 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%p)if(t&1)a=1ll*a*b%p;return a;} 5 int cal(int n,int r=1){tms+=r*n/p;return n?qp(1ll*qp(fac[p-1],n/p)*fac[n%p]%p*cal(n/p)%p,p-1+r):1;} 6 int F(int n,int r=1){tms+=r*n/k;return 1ll*qp(1ll*qp(pr[k-1],n/k)*pr[n%k]%p,p-1+r)*cal(n/k,r)%p;} 7 int main(){ 8 cin>>t>>q>>p;fac[0]=pr[0]=1; 9 for(int r=q;r!=1;k++,r=1ll*r*q%p); 10 for(int i=1;i<p;++i)fac[i]=1ll*fac[i-1]*i%p; 11 for(int i=1,x=q;i<k;++i,x=1ll*x*q%p)pr[i]=pr[i-1]*(x-1ll)%p; 12 while(t-->0)scanf("%d%d",&n,&m),tms=0,ans=1ll*F(n+m)*F(n,-1)%p*F(m,-1)%p,printf("%d\n",tms?0:ans); 13 }