sdoi2017

数字表格
莫比乌斯反演
首先,要把

i=1nj=1mf[gcd(i,j)]i=1nj=1mf[gcd(i,j)]i=1nj=1mf[gcd(i,j)] ∏ i = 1 n ∏ j = 1 m f [ g c d ⁡ ( i , j ) ] ∏ i = 1 n ∏ j = 1 m f [ gcd ( i , j ) ] ∏ i = 1 n ​ ∏ j = 1 m ​ f [ g c d ( i , j ) ]
换一个方向去思考,即不枚举
i,ji,ji,j i , j i , j i , j
,而是枚举
d=gcd(i,j)d=gcd(i,j)d=gcd(i,j) d = g c d ⁡ ( i , j ) d = gcd ( i , j ) d = g c d ( i , j )
。可以得到,在这个表格里,f[d]f[d]f[d] 出现的次数就是
i=1nj=1m[gcd(i,j)=d]i=1nj=1m[gcd(i,j)=d]i=1nj=1m[gcd(i,j)=d] ∑ i = 1 n ∑ j = 1 m [ g c d ⁡ ( i , j ) = d ] ∑ i = 1 n ∑ j = 1 m [ gcd ( i , j ) = d ] ∑ i = 1 n ​ ∑ j = 1 m ​ [ g c d ( i , j ) = d ]

所以:

Ans=df[d]i=1nj=1m[gcd(i,j)=d]Ans=df[d]ni=1mj=1[gcd(i,j)=d]Ans=df[d]i=1nj=1m[gcd(i,j)=d]=df[d]i=1ndj=1md[gcd(i,j)=1]=df[d]ndi=1mdj=1[gcd(i,j)=1]=df[d]i=1dnj=1dm[gcd(i,j)=1] A n s = ∏ d f [ d ] ∑ i = 1 n ∑ j = 1 m [ g c d ⁡ ( i , j ) = d ] A n s = ∏ d f [ d ] ∑ i = 1 n ∑ j = 1 m [ gcd ( i , j ) = d ] A n s = ∏ d ​ f [ d ] ∑ i = 1 n ​ ∑ j = 1 m ​ [ g c d ( i , j ) = d ] = ∏ d f [ d ] ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ⁡ ( i , j ) = 1 ] = ∏ d f [ d ] ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ gcd ( i , j ) = 1 ] = ∏ d ​ f [ d ] ∑ i = 1 ⌊ d n ​ ⌋ ​ ∑ j = 1 ⌊ d m ​ ⌋ ​ [ g c d ( i , j ) = 1 ]

i=1xj=1y[gcd(i,j)=1]i=1xj=1y[gcd(i,j)=1]i=1xj=1y[gcd(i,j)=1]dxdydμ(d)dxdydμ(d)ddxdyμ(d) ∑ i = 1 x ∑ j = 1 y [ g c d ⁡ ( i , j ) = 1 ] ∑ i = 1 x ∑ j = 1 y [ gcd ( i , j ) = 1 ] ∑ i = 1 x ​ ∑ j = 1 y ​ [ g c d ( i , j ) = 1 ] 是 一 个 经 典 问 题 , 它 等 于 ∑ d ⌊ x d ⌋ ⌊ y d ⌋ μ ( d ) ∑ d ⌊ x d ⌋ ⌊ y d ⌋ μ ( d ) ∑ d ​ ⌊ d x ​ ⌋ ⌊ d y ​ ⌋ μ ( d )

即原式等于

df[d]kndkmdkμ(k)df[d]kndkmdkμ(k)df[d]kdkndkmμ(k) ∏ d f [ d ] ∑ k ⌊ n d k ⌋ ⌊ m d k ⌋ μ ( k ) ∏ d f [ d ] ∑ k ⌊ n d k ⌋ ⌊ m d k ⌋ μ ( k ) ∏ d ​ f [ d ] ∑ k ​ ⌊ d k n ​ ⌋ ⌊ d k m ​ ⌋ μ ( k )

继续考虑。可以发现,

ndkndkdknmdkmdkdkm ⌊ n d k ⌋ ⌊ n d k ⌋ ⌊ d k n ​ ⌋ 和 ⌊ m d k ⌋ ⌊ m d k ⌋ ⌊ d k m ​ ⌋
这两个式子不容易直接分块。所以这里令
u=dku=dku=dk u = d k u = d k u = d k
,原式化为:

ukuf[uk]μ(k)numuuk|uf[uk]μ(k)numuukuf[ku]μ(k)unum=u(kuf[uk]μ(k))numu=u(k|uf[uk]μ(k))numu=u(kuf[ku]μ(k))unum ∏ u ∏ k ∣ u f [ u k ] μ ( k ) ⌊ n u ⌋ ⌊ m u ⌋ ∏ u ∏ k | u f [ u k ] μ ( k ) ⌊ n u ⌋ ⌊ m u ⌋ ∏ u ​ ∏ k ∣ u ​ f [ k u ​ ] μ ( k ) ⌊ u n ​ ⌋ ⌊ u m ​ ⌋ = ∏ u ( ∏ k ∣ u f [ u k ] μ ( k ) ) ⌊ n u ⌋ ⌊ m u ⌋ = ∏ u ( ∏ k | u f [ u k ] μ ( k ) ) ⌊ n u ⌋ ⌊ m u ⌋ = ∏ u ​ ( ∏ k ∣ u ​ f [ k u ​ ] μ ( k ) ) ⌊ u n ​ ⌋ ⌊ u m ​ ⌋

这样就可以将

nunuunmumuum ⌊ n u ⌋ ⌊ n u ⌋ ⌊ u n ​ ⌋ 和 ⌊ m u ⌋ ⌊ m u ⌋ ⌊ u m ​ ⌋
的值分块了。令
F[u]=kuf[uk]μ(k)F[u]=k|uf[uk]μ(k)F[u]=kuf[ku]μ(k) F [ u ] = ∏ k ∣ u f [ u k ] μ ( k ) F [ u ] = ∏ k | u f [ u k ] μ ( k ) F [ u ] = ∏ k ∣ u ​ f [ k u ​ ] μ ( k )
,那么就可以预处理FFF 的前缀积,计算区间的积时使用逆元计算。

不知为什么,不开longlongtle

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const long long N=1000000,mod=1000000007;
long long pr[N+10],tot,miu[N+10],n,m,f[N+10],g[N+10],F[N+10],T,ans;bool ip[N+10];
inline long long read()
{
    long long x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
long long fastpow(long long a,long long k){long long ans=1;
    while(k){
        if(k&1) ans=(1ll*ans*a)%mod;
        a=(1ll*a*a)%mod;k>>=1;
    }
    return ans%mod;
}
void pre(){miu[1]=f[1]=g[1]=ip[1]=ip[0]=F[0]=F[1]=1;
    for(long long i=2;i<=N;i++) {
        f[i]=(f[i-1]+f[i-2])%mod; g[i]=fastpow(f[i],mod-2);F[i]=1;
        if(!ip[i])  pr[++tot]=i,miu[i]=-1;
        for(long long j=1;j<=tot&&i*pr[j]<=N;j++){
            ip[pr[j]*i]=1;miu[pr[j]*i]=i%pr[j]?-miu[i]:0;
            if(i%pr[j]==0) break;
        }
    }
    for(long long i=1;i<=N;i++){
        if(!miu[i]) continue;
        for(long long j=i;j<=N;j+=i) F[j]=1ll*F[j]*(miu[i]==1?f[j/i]:g[j/i])%mod;
    }
    for(long long i=2;i<=N;i++) F[i]=1ll*F[i]*F[i-1]%mod; 
}
int main(){pre();
    T=read();
    while(T--){ans=1;
        n=read(),m=read();if(n>m) swap(n,m);
        for(long long i=1,j,tmp;i<=n;){
            j=min(n/(n/i),m/(m/i));
            tmp=1ll*F[j]*fastpow(F[i-1],mod-2)%mod;
            ans=(1ll*ans*fastpow(tmp,(n/i)*(m/i)%(mod-1)))%mod;
            i=j+1;
        }
        printf("%lld\n",ans);
    }
}

[SDOI2017]序列计数

f[i][j]+=f[i1][q]((q+k)==px,xR) f [ i ] [ j ] + = f [ i − 1 ] [ q ] ( ( q + k ) == p ∗ x , x ∈ R )

很显然
f[i][j]=f[i][q]pp f [ i ] [ j ] = f [ i ] [ q ] ∗ 一 个 p ∗ p 的 矩 阵

就用矩阵快速幂了

// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<cstring>
const int N=2*1e7+9,mod=20170408;
int ip[N],pr[N],tot,miu[N],n,m,p,s[101],ans;
void pre(){ip[1]=1;
    for(int i=2;i<=N;i++) {
        if(!ip[i])  pr[++tot]=i;
        for(int j=1;j<=tot&&i*pr[j]<=N;j++){
            ip[pr[j]*i]=1;  if(i%pr[j]==0) break;
        }
    }
}
struct Mat{
    int mat[101][101];Mat(){memset(mat,0,sizeof mat);}
    Mat operator *(Mat b)const{
        Mat ans;
        for(int i=0;i<p;i++) for(int k=0;k<p;k++)for(int j=0;j<p;j++)
        ans.mat[i][j]=(ans.mat[i][j]+1ll*mat[i][k]*b.mat[k][j]%mod)%mod;


        return ans;
    }
}tmp1,tmp2,tmp3;
int main(){pre();
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=m;i++) s[i%p]++;tmp1.mat[0][0]=1;
    for(int i=0;i<p;i++) for(int j=0;j<p;j++) tmp2.mat[i][(i+j)%p]=s[j]%mod;
    for(int i=n;i;i>>=1,tmp2=tmp2*tmp2) if(i&1) tmp1=tmp1*tmp2;
    ans=tmp1.mat[0][0];memset(s,0,sizeof s);
    for(int i=1;i<=m;i++) if(ip[i])s[i%p]++;tmp3.mat[0][0]=1;
    for(int i=0;i<p;i++) for(int j=0;j<p;j++) tmp2.mat[i][(i+j)%p]=s[j]%mod;
    for(int i=n;i;i>>=1,tmp2=tmp2*tmp2) if(i&1) tmp3=tmp3*tmp2;
    ans-=tmp3.mat[0][0];
    printf("%d",(ans+mod)%mod);
}

新生舞会

01分数规划+带权二分图匹配

#include<cstdio>
#include<cstring> 
#include<queue>
using namespace std;
const int M=210;queue<int>q;const double INF=20000000.0;
int n,S,T,mflow;bool vis[M];double cos[M*M],mcost,a[M][M],b[M][M],dis[M];
int nex[M*M],head[M],to[M*M],cap[M*M],pre[M],id[M],tot,flow[M];
void add(int x,int y,int z,double w){
    nex[++tot]=head[x];
    cos[tot]=w;cap[tot]=z;
    head[x]=tot;to[tot]=y;
}
int spfa(int s,int t){
    memset(vis,0,sizeof vis);memset(pre,-1,sizeof pre);
    for(int i=0;i<=2*n+1;i++) dis[i]=INF;
    dis[s]=0;q.push(s);vis[s]=1;flow[s]=INF;
    while(!q.empty()){
        int x=q.front();q.pop(),vis[x]=0;
        for(int i=head[x],tmp;i>=2;i=nex[i]){
            if(dis[tmp=to[i]]>dis[x]+cos[i]&&cap[i]){
                dis[tmp]=dis[x]+cos[i];id[tmp]=i;
                pre[tmp]=x;flow[tmp]=min(flow[x],cap[i]);
                if(!vis[tmp]) q.push(tmp),vis[tmp]=1;
            }
        }
    }
    return dis[t]<INF;
}
bool check(double x){
    tot=1;mflow=mcost=0;memset(head,0,sizeof head);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) add(i,j+n,1,x*b[i][j]-a[i][j]),add(j+n,i,0,a[i][j]-x*b[i][j]);
    for(int i=1;i<=n;i++) add(S,i,1,0),add(i,S,0,0),add(i+n,T,1,0),add(T,i+n,0,0);
    while(spfa(S,T)){int k=T;
        while(k!=S){
            cap[id[k]]-=flow[T],cap[id[k]^1]+=flow[T];
            k=pre[k];
        }
        mflow+=flow[T];mcost+=(double)flow[T]*dis[T];
    }
    return -mcost>=0.0000001;
}
int main(){
    scanf("%d",&n);T=2*n+1;
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) scanf("%lf",&a[i][j]);
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++) scanf("%lf",&b[i][j]);
    double l=0,r=1e5;
    while(l+0.0000001<=r){double mid=(l+r)/2;
        if(check(mid)) l=mid;else r=mid;
    }
    printf("%lf",l);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值