3.17省选模拟
心情真的会因为一句祝福的话而开心!
$T1$
考场上观察样例,发现是个类似三元环的东西
其实这个东西放到图上或许就可做很多
就是有边$x->y,y->z$连接$z->x$
然后统计一共多少边
考试时候被卡在了是不是应该把行列拆成俩点的问题,甚至自己把自己$1->1$这种情况给卡了
考虑为什么这两个东西是等价的,其实你这个过程一直都是找边,发现有可以连的边,连上,继续找的过程
也就不需要搞一些奇奇怪怪的分成行列的操作了
考试时候想到这就卡了,然后不知道如何快速计数
思考一下这个过程,就是给你一张初始图,然后往外连边统计的过程
正解:
对原图进行三染色,依次染色$1->2->3$
对于染完色的统计贡献,如果发现染色失败(就是染色冲突了),就是一个完全图了,黑边是初始,红边是新加边
如果只有两种颜色,那么显然是只有两层,不存在环就是原来的边数了
如果染色成功,那么考虑最后的图会变成什么样子
考虑这个这个无论怎么连,颜色的相对位置关系是不变的,最后的图就是所有颜色的组合加一下就好了
#include<bits/stdc++.h> #define int long long #define MAXN 200005 using namespace std; int head[MAXN],nxt[MAXN],val[MAXN],to[MAXN],tot; int cnt[3],col[MAXN],Num1,Num2,Ans,n,m; bool vis[MAXN],flag; void add(int u,int v,int w) { tot++; to[tot]=v; val[tot]=w; nxt[tot]=head[u]; head[u]=tot; } void dfs(int now) { cnt[col[now]]++; Num1++; vis[now]=true; for(int i=head[now];i;i=nxt[i]) { int y=to[i]; if(val[i]==1) Num2++; // cout<<"nowbei: "<<now<<" "<<y<<endl; if(!vis[y]) { col[y]=(col[now]+val[i]+3)%3; dfs(y); } else { if(col[y]!=(col[now]+val[i]+3)%3) { flag=false; } } } } int u,v; signed main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=m;i++) { scanf("%lld%lld",&u,&v); add(u,v,1); add(v,u,-1); } for(int i=1;i<=n;i++) { if(vis[i]) continue; cnt[0]=cnt[1]=cnt[2]=Num1=Num2=0; flag=true; dfs(i); if(flag==false) Ans+=Num1*Num1;//cout<<"bei: "<<i<<" "<<Num1<<endl; else if(cnt[0]&&cnt[1]&&cnt[2]) Ans+=(cnt[0]*cnt[1]+cnt[1]*cnt[2]+cnt[2]*cnt[0]);//cout<<"cnt: "<<cnt[0]<<" "<<cnt[1]<<" "<<cnt[2]<<endl; else Ans+=Num2; } cout<<Ans<<endl; }
$T2$
考场上想到拆$\phi$
奈何没拆出来,一个好玩的式子
$\phi(i\times j)=\frac{\phi(i)\phi(j)gcd(i,j)}{\phi(gcd(i,j))}$
这么显然的式子我推个寄啊(划去巨长证明)
听别人讲了一遍,我大概可以顺一遍
先考虑一下原式
$\sum_{i=1}^n\sum_{j=1}^n(dis(i,j)\phi(a_ia_j))$
愉快的拆式子环节
$\sum_{i=1}^n\sum_{j=1}^ndis(i,j)\frac{\phi(a_i)\phi(a_j)gcd(a_i,a_j)}{\phi(gcd(a_i,a_j))}$
经典枚举$gcd$
$\sum_{d=1}^n\sum_{i=1}^n\sum_{j=1}^ndis(i,j)\frac{\phi(a_i)\phi(a_j)d}{\phi(d)}[gcd(a_i,a_j)=d]$
$\sum_{d=1}^n\frac{d}{\phi(d)}\sum_{i=1}^n\sum_{j=1}^ndis(i,j)\phi(a_i)\phi(a_j)[gcd(a_i,a_j)=d]$
设$f(d)=\sum_{i=1}^n\sum_{j=1}^ndis(i,j)\phi(a_i)\phi(a_j)[gcd(a_i,a_j)=d]$
就是所有权值$gcd=d$的点对在这里统计一下
首先我们最后的式子确定了,我们又可以把$\phi(ij)$拆出来,我们就可以根据$gcd$把点对都恰好分到一组里面
设$F(x)=\sum_{x|d}f(d)$
就是所有$x$的倍数的$f(d)$和
反演一下
$f(x)=\sum_{x|d}F(d)\mu(\frac{d}{x})$(话说我只知道莫反有因数反演现在竟然还有倍数反演,长知识了...)
我们要求$f(x)$,那就需要求出$x$的所有倍数的$F(d)$
那么考虑枚举$x$,对$x$的所有倍数建立虚树,为什么是所有$x$的倍数建立虚树
考虑我求这个$x$需要所有的倍数$F(d)$,而$F(d)$计算是所有
我发现这个式子越推越反人类...所以上面式子废弃,重新推式子
由于又发现,其实权值也是排列,那么就把权值设成编号就好了,反正最后算的时候答案不变
$\sum_{g=1}^n\sum_{x=1}^n\sum_{y=1}^ndis(x,y)\frac{\phi(x)\phi(y)g}{\phi(g)}[gcd(x,y)==g]$
$\sum_{g=1}^n\sum_{x=1}^{n/g}\sum_{y=1}^{n/g}dis(xg,yg)\frac{\phi(xg)\phi(yg)g}{\phi(g)}[gcd(x,y)==1]$
$\sum_{g=1}^n\sum_{x=1}^{n/g}\sum_{y=1}^{n/g}dis(xg,yg)\frac{\phi(xg)\phi(yg)g}{\phi(g)}\sum_{p|x,p|y}\mu(p)$
$\sum_{g=1}^n\sum_{x=1}^{n/g}\sum_{y=1}^{n/g}\sum_{p|x,p|y}dis(xg,yg)\frac{\phi(xg)\phi(yg)g}{\phi(g)}\mu(p)$
$\sum_{g=1}^n\sum_{p=1}^n\sum_{x=1}^{n/(g\times p)}\sum_{y=1}^{n/(g\times p)}dis(xgp,ygp)\frac{\phi(xgp)\phi(ygp)g}{\phi(g)}\mu(p)$
经典$dp=q$
$\sum_{q=1}^{n}\mu(\frac{q}{g})\frac{g}{\phi(g)}\sum_{x=1}^{n/q}\sum_{y=1}^{n/q}dis(xq,yq)\phi(xq)\phi(yq)$
那么直接对于每个$q,$求一下后面式子的值就好了
发现对于每个$q$,我们用到的点只有倍数,复杂度$O(nln(n))$
具体过程的话,就是对后面的东西建个虚树,求一下贡献就好了
后面还是简单拆一下式子
建完虚树之后,我们对于每个点就把$\phi(x)$当做点权
对于这个式子无非是这个虚树上任意两点距离乘上权值就好了
计算式子
$\sum_x\sum_ydis(x,y)\times val_x\times val_y$
距离拆式子
$dis(x,y)=dep_x+dep_y-2\times dep_{lcd(x,y)}$
带回式子
$\sum_x\sum_y(dep_x+dep_y-2\times dep_{lca(x,y)})\times val_x\times val_y=$
$\sum_x\sum_ydep_x\times val_x\times val_y+$
$\sum_x\sum_ydep_y\times val_x\times val_y-$
$2\times\sum_x\sum_ydep_{lca(x,y)}\times val_x\times val_y$
前两个式子显然直接求就好了,后面这个简单跑一个树形$dp$就解决了
后面这个式子
直接用一个$dp$转移
$dp[x]$表示以$x$为根的子树这个权值和
那么对这个东西跑一下$dp$
$dp[x]+=dp[y]+\sum_{i\in son_x}dep_x\times val_i\sum_{j\in son_y}val_j$
$T3$
考场上大概想到了群论来解决这个问题
长度为$n$的字符串都是由大小在$1-k$的字符组成,那么能够任意选的部分只有一半
本质不同的回文串的个数是$k^{\lfloor\frac{n}{2}\rfloor}$
那么这时候考虑旋转,能够产生的串数是最短循环节长度,想到这一部分我就卡住了
那么既然有这个循环节长度的限制,我们就枚举这个限制就好了
还是考虑只要最后不重不漏计算出每个贡献就好了,其实还是考虑一个很常见的想法
就是转化角度计算答案,那么考虑可以按照不同的性质分组,只需要每个都被统计一次就好了
考试时候也想到了通过枚举最小循环节来计算答案,显然最小循环节长度是唯一的,那么就枚举最小循环节来统计贡献了
那考虑怎么统计贡献,首先循环节显然要$n\mod l=0$,那么就开始枚举循环节
设满足$l$的个数是$f[l]$和,那么答案是$f[l]\times l$
那么直接算一下就好了
考虑我们确定了循环节$l$,由于又要是回文串,我们的循环节必然是回文,如果不是回文就寄了,那么长度为$l$的回文串方案数是$k^{\frac{l}{2}},$这时候这部分答案是重复的,比如说,目前答案是$l$,其实你会枚举到更小的循环节的情况然后算上了一些这样的情况,那么显然我们长的能包括小的所有情况,那么我们只需要把小的全部减去,显然减去的这部分是自己的因数,那么直接减就好了
至于为什么偶数时候$w[i]/2$,那么就分为$2112$这个循环节可以被统计两遍$2112$和$1221$那么显然了
#include<bits/stdc++.h> #define mod 1000000007 #define int long long #define MAXN 10005 using namespace std; int n,k,tot,Ans,w[MAXN],f[MAXN]; int my_pow(int a,int b) { int res=1; while(b) { if(b&1) { res=(res*a)%mod; } a=(a*a)%mod; b>>=1; } return res; } signed main() { cin>>n>>k; for(int i=1;i*i<=n;i++) { if(n%i==0) { w[++tot]=i; if(i*i!=n) w[++tot]=n/i; } } sort(w+1,w+1+tot); for(int i=1;i<=tot;i++) { f[i]=my_pow(k,(w[i]+1)/2); for(int j=1;j<i;j++) { if(w[i]%w[j]==0) f[i]=(f[i]-f[j]+mod)%mod; } if(w[i]%2==0) { Ans+=(w[i]/2)*f[i]%mod; Ans%=mod; } else { Ans+=w[i]*f[i]%mod; Ans%=mod; } } cout<<Ans<<endl; }