【日常摸鱼】牛客挑战赛46(未完)

【日常摸鱼】牛客挑战赛46

前言

日渐消沉,随便做几题,假装我还在练acm。

A 奇怪的计算器

水题略了

B 最小的指数

链接

https://ac.nowcoder.com/acm/contest/9510/B

题意

询问正整数x的质因数的指数的最小值。数据组数1e5,n的范围1e18。

题解

这个操作还挺有趣的。。
先筛掉4000以内的所有质数,然后剩下的质数的指数不会超过4。
然后如果最小指数是4,那肯定能开4次方,(其他情况会有更小的指数)
如果最小指数是3,那肯定能开3次方,(其他情况会有更小的指数)
如果最小指数是2,那就要求能开2次方,但不能开4次方。
其余的就是1次的了。

(Code)

#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
#define LD long double
using namespace std;
const int N=3e5+10;
const LL INF=1e18;
LL read(){
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
int cnt=0;
bool vis[4050];
int p[4050];
LL qpow(LL x,int y){
    LL res=1;
    for(int i=1;i<=y;++i) {
        if(res<=INF/x) res=res*x;
        else res=INF;
    }
    return res;
}
LL sqr(LL x,int y){
    LL l=1,r=1e9,mid;
    while(l!=r){
        mid=(l+r+1)>>1;
        if(qpow(mid,y)<=x) l=mid;
        else r=mid-1;
    }
    return l;
}
int main(){
    for(int i=2;i<=4000;++i){
        if(!vis[i]){
            p[++cnt]=i;
            for(int j=i+i;j<=4000;j+=i) vis[j]=1;
        }
    }
    int T=read();LL n,x,y,mn;
    while(T--){
        n=read();mn=100;
        if(n==1){
            puts("0");
            continue;
        }
        //cout<<n<<endl;
        for(int i=1;i<=cnt;++i){
            x=0;
            while(n%p[i]==0){
                n=n/p[i];
                ++x;
            }
            if(x)mn=min(mn,x);
        }
        //cout<<n<<endl;
        if(n>1){
            x=sqr(n,4);
            if(x*x*x*x==n) mn=min(mn,(LL)4);
            else{
                x=sqr(n,3);
                if(x*x*x==n) mn=min(mn,(LL)3);
                else{
                    x=sqr(n,2);
                    if(x*x==n) mn=min(mn,(LL)2);
                    else mn=min(mn,(LL)1);
                }
            }
        }
        print(mn);puts("");
    }
    return 0;
}

C 排列

链接

https://ac.nowcoder.com/acm/contest/9510/C

题意

求长度为n,并且恰好有k个超级逆序对的排列的个数。n,k不超过500。超级逆序对就是两个数的数值之差至少为2的逆序对。

题解

就是前缀和优化一下DP就行了。

(Code)

#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
#define LD long double
using namespace std;
const int N=3e5+10;
const LL P=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
void add(int &x,int y){
    x+=y;if(x>=P)x-=P;
}
LL qpow(LL x,LL y){
    LL re=1;
    while(y){
        if(y&1) re=re*x%P;
        x=x*x%P;y>>=1;
    }
    return re;
}
int n,K;
int f[510][510][510+510];

int main(){
    scanf("%d%d",&n,&K);
    f[1][1][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=i;j>=1;--j){
            for(int k=0;k<=K;++k){
                if(k>0) add(f[i][j][k],f[i][j+1][k-1]);
                if(f[i][j][k]){
                    add(f[i+1][i+1][k],f[i][j][k]);
                    add(f[i+1][j][k+(i-j)+1],P-f[i][j][k]);
                    add(f[i+1][j][k+(i-j)],f[i][j][k]);
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i) add(ans,f[n][i][K]);
    printf("%lld
",qpow((LL)ans,P-2));
    return 0;
}

D 数列

链接

https://ac.nowcoder.com/acm/contest/9510/D

题意

给一个长度为n的数列,求区间内每个数出现次数恰好为k的倍数的区间数。n,ai,k不超过1e6.

题解

给每个位置赋一个随机值,并且使任意连续的k个一样的数的随机值之和为0.
然后询问相当于有多少区间的赋值之和为0,这个就很容易做了。

(Code)

#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
#define LD long double
using namespace std;
const int N=1e6+10;
const LL P=998244353;
const LL INF=1e18;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
unordered_map<LL,LL> mp;
int n,K;
int a[N],c[N];
vector<LL> ve[N];
LL sum[N],b[N];
int main(){
    srand(time(0));
    LL x,ans=0;
    scanf("%d%d",&n,&K);mp[0]=1;
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        ++c[a[i]];
        if(c[a[i]]<K){
            x=rand()*rand()%INF*rand()%INF;
            sum[a[i]]+=x;
            ve[a[i]].push_back(x);
        }
        else if(c[a[i]]==K){
            sum[a[i]]=-sum[a[i]];
            ve[a[i]].push_back(sum[a[i]]);
        }
        b[i]=ve[a[i]][(c[a[i]]+K-1)%K];
        b[i]+=b[i-1];
        ans+=mp[b[i]];
        mp[b[i]]++;
    }
    printf("%lld
",ans);
    return 0;
}

E 反演

链接

https://ac.nowcoder.com/acm/contest/9510/E

题解

题目要求的式子 (sum_{i=1}^{n}sum_{j|m!}sigma_{0}(ij))
两数乘积的约数个数是可以转化成公约数有关的式子的。
(sum_{i=1}^{n}sum_{j|m!}sigma_{0}(ij)) (=) (sum_{i=1}^{n}sum_{j|m!}sum_{x|i}sum_{y|j}[(x,y)=1])
然后就是喜闻乐见的莫比乌斯反演推式子的套路。。这里直接给出推完之后的式子
(sum_{t|m!}mu(t)sum_{y|frac{m!}{t}}sigma_{0}(frac{m}{ty})sum_{i=1}^{lfloor frac{n}{t} floor}lfloor frac{n}{it} floor)
由于m不超过100,也就是说质数最多只有25个,有用的莫比乌斯函数也只有(2^{25})个,于是枚举莫比乌斯函数不为0的项加起来就可以了。

(Code)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+10;
const LL P=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
LL n,nn;
LL inv[100005],c[110];
int m,cnt;
int vis[110];
int p[110];
LL mp[1000000],MP[1000000];
LL F(LL x){
    if(x<=nn&&mp[x]) return mp[x];
    if(x>nn&&MP[(n/x)]) return MP[(n/x)];
    LL res=0,v;
    for(LL i=1,j;i<=x;i=j+1){
        v=x/i;
        j=x/v;
        res+=v*(j-i+1);
    }
    res=res%P;
    if(x<=nn) mp[x]=res;
    if(x>nn) MP[(n/x)]=res;
    return res;
}
LL ans;
void sol(int id,LL t,LL mu,LL sum){
    if(t>n) return;
    if(id>cnt){
        ans+=mu*F(n/t)%P*sum%P;
        return; 
    }
    sol(id+1,t,mu,sum);
    sol(id+1,t*(LL)p[id],P-mu,sum*inv[c[id]+2]%P*c[id]%P);
}
int main(){
    inv[0]=inv[1]=1;
    for(LL i=2;i<=100000;++i) inv[i]=(P-P/i)*inv[P%i]%P;
    cin>>n>>m;nn=sqrt(n);cnt=0;ans=0;
    for(int i=2;i<=m;++i){
        if(!vis[i]){
            p[++cnt]=i;
            for(int j=i+i;j<=m;j+=i){
                vis[j]=1;
            }
        }
    }
    int x;LL tot=1;
    for(int i=1;i<=cnt;++i){
        c[i]=0;x=m;
        while(x>0){
            x=x/p[i];
            c[i]+=x;
        }
        tot=tot*(c[i]+2)%P*(c[i]+1)%P*inv[2]%P;
    }
    sol(1,1LL,1LL,tot);
    ans=(ans%P+P)%P;
    printf("%lld
",ans);
    return 0;
}

F 柠檬树

链接

https://ac.nowcoder.com/acm/contest/9510/F

题解

我也还不会,这个LCT太神了QAQ

原文地址:https://www.cnblogs.com/Yuigahama/p/14197564.html