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;
}

 

posted @ 2022-03-17 21:48  Authentic_k  阅读(33)  评论(0编辑  收藏  举报