CXJY-Day 4

Author Avatar
Axell 8月 01, 2019
  • 在其它设备中阅读本文章

FLAG: T3还不会做,记得填坑

闯关大赛(competition)

【题目描述】

因为小A的游戏技巧太过高超,所以他被邀请参加第2019届头号玩家邀请赛,与众多游戏好手同台竞技,决出2019年的头号玩家。
这场比赛一共有n道题目,都是某个游戏中的某个高难度关卡,小A在看到题目后就预估出了自己需要多长时间来解决这个关卡(以秒计时),但是由于那一天小A感冒了,虽然用的是自己最顺手的设备,几乎不怎么可能出现操作失误的问题,但是他有50%的概率会在一个题目中打一个喷嚏,导致他要多花1秒来挽回这个失误。
由于比赛的特殊要求,题目必须按顺序一道一道做下来。
比赛总时长为T,而小A由于担心这个不确定因素,想让你帮他算一算他期望完成的题目个数。
注意,如果在第T秒完成了一道题目,也算是完成了这道题目。

【输入数据】

第一行两个整数n,T,分别表示题目的数量,比赛的事件总长。
接下来一行n个整数,分别表示每一道题目的时长。

【输出数据】

一个整数表示完成题目的期望个数。
答案对$(1e9+7)$取模,即设答案化为最简分式后的形式为$a/b$,其中a和b的互质。输出整数x使得$bx≡a mod (1e9+7)$且$0≤x \le 1e9+7$。可以证明这样的整数x是唯一的。

【样例输入】

3 5
2 2 2

【样例输出】

750000007

【数据范围】

对于30%的数据,$n≤20,T≤1e6$。
对于60%的数据,$n≤1000,T≤1e9$。
对于100%的数据,$n≤100000,T≤1e12$。每道题目的时长$≤1e9$。

题解

30% 期望的线性加性质,可以dp,$dp[i][j]$表示当前做完第i个任务,时间为j的概率,最后全部求和即可
60% 算出每个点可以完成的概率,即它对答案的贡献,$p=\sum_{i=0}^{T-sumt}{C_c^i}*0.5^c$,其中c为当前的点,sumt为c及之前的时间和
100% 优化60%的求和公式即可,由杨辉三角可知$$\sum_{i=0}^{i}C_{c+1}^{i}=2*\sum_{i=0}^m{C_c^i}-C_c^m$$由于每次的上界$min(i,T-sumt)$变化是单调的,考虑记下每次的前缀和,每次先把$sum=sum*2-C_i^{lastm}$,然后根据新的状态进行增减,并更新lastm即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll mod=1e9+7;
ll n,t,a[100005],p[100005],ans;
ll power(ll a,ll b){
    ll ret=1;
    while (b){
        if (b&1) ret=ret*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ret;
}

ll C(ll a,ll b){
    return p[a]*power(p[a-b],mod-2)%mod*power(p[b],mod-2)%mod;
}

int main(){
    freopen("competition.in","r",stdin);
    freopen("competition.out","w",stdout);
    cin>>n>>t;
    p[0]=1;
    for (int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        p[i]=p[i-1]*i%mod;
    }
    int lastm=0; ll sumc=1,lastc=1,sumt=0;
    for (int i=1;i<=n;++i){
        sumc=(sumc*2-lastc)%mod;
        if (sumc<0) sumc+=mod;
        sumt+=a[i];
        ll nowm=min((ll)i,t-sumt);
        if (nowm<0) break;
        if (nowm>lastm){
            for (int j=lastm+1;j<=nowm;++j){
                lastc=C(i,j);
                sumc=(sumc+lastc)%mod;
            }
        }
        if (lastm>nowm){
            for (int j=lastm;j>=nowm+1;--j){
                lastc=C(i,j);
                sumc=sumc-lastc;
                if (sumc<0) sumc+=mod;
            }
        }
        lastm=nowm;
        lastc=C(i,nowm);
        ans=(ans+sumc*power(power(2,i),mod-2)%mod)%mod;
    }
    cout<<ans<<endl;
    return 0;
}

户口调查(research)

【题目描述】

小A《都市:天际线》的城市现在人口爆炸,急需做一个人口普查来制定下一步人口计划了。
在调查的过程中,小A看到很多人的生日都是相同的,所以他想要知道,如果有k个人,那么这k个人里面存在两个生日相同的人的概率。
《都市:天际线》这款游戏里面一年比较长,一年有天,每个人的生日相互独立。

【输入数据】

一行两个整数n,k。

【输出数据】

输出两个整数A,B,答案是A/B,且A,B要对(1e6+3)取模,取模前A,B互质。

【样例输入1】

3 2

【样例输出1】

1 8

【样例输入2】

4 3

【样例输出2】

23 128

【数据范围】

对于20%的数据,$1≤n,k≤10$。
对于40%的数据,$1≤n≤10,1≤k≤1000$。
对于另20%的数据,$1≤n≤20$。
对于100%的数据,$1≤n,k≤1e18$。

题解

概率为1-每个人生日都不同的方案数
考虑求每个人生日都不同的方案数$$\frac{A_{2^n}^k}{(2^n)^k}=\frac{(2^n)(2^n-1)(2^n-2)\dots(2^n-k+1)}{2^{nk}}$$
由于要先约分,因此要先求出分子中2的倍数,可以发现因子个数为$$n+Cnt_1+Cnt_2+\dots+Cnt_{k-1}$$即每一项的2的因子数由被减的决定,统计时直接$n+(k-1)/2+(k-1)/4+\dots$
发现p很小,当$k>=p$时,$A_{2^n}^k$是连续k个自然数的乘积,就等于0,否则直接暴力求即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n,k,mod=1e6+3,ans1,ans2;

ll power(ll a,ll b){
    ll ret=1;
    while (b){
        if (b&1) ret=ret*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return ret;
}

ll get2(ll x){
    ll ans=0;
    while (x){
        ans+=(x/2)%(mod-1);
        ans%=mod-1;
        x>>=1;
    }
    return (ans+n)%(mod-1);
}

int main(){
    freopen("research.in","r",stdin);
    freopen("research.out","w",stdout);
    cin>>n>>k;
    ll c2=get2(k-1);
    ans2=n%(mod-1)*(k%(mod-1))%(mod-1)-c2;
    if (ans2<0) ans2+=mod-1;
    ans2=power(2,ans2);
    if (k>=mod) ans1=0;
    else{
        ll fl=power(2,n);
        ans1=1;
        for (int i=0;i<k;++i){
            ll tt=fl-i;
            if (tt<0) tt+=mod;
            ans1=ans1*tt%mod;
        }
        ans1=ans1*power(power(2,c2),mod-2)%mod;
    }
    ans1=ans2-ans1;
    if (ans1<0) ans1+=mod;
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}

挑战小B(gcdlcm)

【题目描述】

在被小B的问题击败后,小A开始努力学习了,数论是他最近比较喜欢的板块,所以他又想出了一道题目,想要用来击败小B,重新得到自信。
问题是:有一个长度为n的数组A,每一个元素可以取1~m中的任意一个数,很显然,一共有个这样的数组。
问题一共有两种:
对于给定的正整数k,有多少个数组A满足$gcd(Ai)(i=1,2,…,n)=k$
对于给定的正整数k,有多少个数组A满足$k|lcm(Ai)(i=1,2,…,n)$
但是这种题目太简单了,很容易就被小B秒掉了,而小B又加强了一下这道题,请对$[L,R]$范围内的k,求出某种问题的答案,并加起来回答整个问题。
因为答案会很大,所以请对$(1e9+7)$取模。

【输入数据】

第一行两个整数T,type表示数组组数以及数据类型($type=1$时回答gcd的问题的答案,$type=2$时回答lcm的问题的答案)。
接下来T行每行四个整数$n,m,L,R(1<=L<=R<=m)$表示一次询问。

【输出数据】

T行每行一个整数ans,表示第i次询问的答案,应对$(1e9+7)$取模。

【样例输入1】

5 1
5 5 1 5
5 5 2 5
5 5 3 5
5 5 4 5
5 5 5 5

【样例输出1】

3125
34
3
2
1

【样例输入2】

5 2
5 5 1 5
5 5 2 5
5 5 3 5
5 5 4 5
5 5 5 5

【样例输出2】

12310
9185
6303
4202
2101

题解

type=1
$dp[i][j]$为前i个数gcd为j的方案数,然后转移
$f[i]$表示n个数的gcd为i的方案数$$f[i]=(\lfloor m/i \rfloor)^n-f[2*i]-f[3*i]\dots $$从后往前算即可
或者$g[i]$表示n个不大于i的数gcd为1的方案数,$$g[i]=i^n-g[i/2]+g[i/3]\dots$$即总方案数减去gcd为2,3,4…的方案
由此可以发现$f[i]=g[m/i]$,因此当$m/i=m/j$时,$f[i]=f[j]$,由于$\lfloor m/i \rfloor$只有$\sqrt{n}$种取值,因此可以加速递推
type=2
容斥原理,若设$k=p^a,ans=m^n-(m-m/(p^a))^n$,若$k=p^a*q^b,ans=m^n-(m-m/(p^a))^n-(m-m/(q^b))^n+(m-m/(p^a)-m/(q^b)+m/(p^a*q^b))^n$,因为最多有4个因数,因此3,4情况同理

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=(int)1e7+5,P=(int)1e9+7;
void Add(int &x,int y){
    x+=y;
    if(x>=P)x-=P;
}
void Del(int &x,int y){
    x-=y;
    if(x<0)x+=P;
}
int fast(int x,int p){
    int re=1;
    while(p){
        if(p&1)re=(long long)re*x%P;
        x=(long long)x*x%P,p>>=1;
    }return re;
}
int Mod(int x){
    if(x<0)x+=P;
    return x;
}
int n,m,l,r;
int vis[M],mu[M],sum[M],prime[M/10],psz;
struct SHUI{
    int fst[M],ps[M],TIM;
    int fast(int x,int p){
        int &re=fst[x];
        if(ps[x]==TIM)return re;
        ps[x]=TIM;
        re=1;
        while(p){
            if(p&1)re=(long long)re*x%P;
            x=(long long)x*x%P,p>>=1;
        }return re;
    }
    int g(int x){
        int re=0;
        for(int i=1,a,b;i<=x;){
            a=x/i;
            b=x/a;
            Add(re,(long long)(sum[b]-sum[i-1]+P)*fast(a,n)%P);
            i=b+1;
        }
        return re;
    }
    void solve(){
        TIM++;
        int ans=0;
        for(int i=l,a,b;i<=r;){
            a=m/i;
            b=m/a;
            if(b>r)b=r;
            Add(ans,(long long)g(a)*(b-i+1)%P);
            i=b+1;
        }
        printf("%d\n",ans);
    }
}P50;
int cas,tp;
void init1(){
    mu[1]=1;
    for(int i=2,tp;i<M;i++){
        if(vis[i]==0)prime[psz++]=i,mu[i]=-1;
        tp=-mu[i];
        for(int j=0,t;j<psz&&((t=prime[j]*i)<M);j++){
            vis[t]=1;
            if(i%prime[j]==0){
                mu[t]=0;
                break;
            }
            mu[t]=tp;
        }
    }
    for(int i=1;i<M;i++)sum[i]=sum[i-1]+mu[i];
}
void solve1(){
    init1();
    while(cas--)scanf("%d %d %d %d",&n,&m,&l,&r),P50.solve();
}
struct UHSI{
    int pd[205][205],fac[205];
    int dp[205][8];
    int pm[5],bin[5];
    UHSI(){
        for(int i=0;i<5;i++)bin[i]=(1<<i);
        fac[0]=1;
        for(int i=1;i<205;i++)fac[i]=(long long)fac[i-1]*i%P;
        pd[0][0]=1;
        for(int i=1;i<205;i++)
            for(int j=1;j<205;j++)
                Add(pd[i][j]=pd[i-1][j-1],(long long)pd[i][j-1]*i%P);
        for(int i=1;i<205;i++)
            for(int j=1;j<205;j++)
                pd[i][j]=(long long)pd[i][j]*fac[i]%P;
    }
    int calc(int x){
        if(x==1)return fast(m,n);
        memset(dp,0,sizeof(dp));
        int sz=0;
        for(int i=0;prime[i]*prime[i]<=x;i++)
            if(x%prime[i]==0){
                pm[sz]=1;
                while(x%prime[i]==0)x/=prime[i],pm[sz]*=prime[i];
                sz++;
            }
        if(x>1)pm[sz++]=x;
        int re=0,up=(1<<sz)-1;
        dp[0][0]=1;
        for(int i=1,s1;i<=m;i++){
            s1=0;
            for(int k=0;k<sz;k++)
                if(i%pm[k]==0)
                    s1|=bin[k];
            for(int j=i-1;j>=0;j--)
                for(int l=up;l>=0;l--)
                    Add(dp[j+1][s1|l],dp[j][l]);
        }
        for(int i=1;i<=m;i++)
            Add(re,(long long)dp[i][up]*pd[i][n]%P);
        return re;
    }
    void solve(){
        int ans=0;
        for(int i=l;i<=r;i++)Add(ans,calc(i));
        printf("%d\n",ans);
    }
}P20;
struct IUHS{
    int pm[5],bin[5];
    int stk[5],ssz;
    IUHS(){
        for(int i=0;i<5;i++)bin[i]=(1<<i);
    }
    int calc(int x){
        if(x==1)return fast(m,n);
        int sz=0;
        for(int i=0;prime[i]*prime[i]<=x;i++)
            if(x%prime[i]==0){
                pm[sz]=1;
                while(x%prime[i]==0)x/=prime[i],pm[sz]*=prime[i];
                sz++;
            }
        if(x>1)pm[sz++]=x;
        int re=fast(m,n),up=(1<<sz)-1;
        for(int i=1,t;i<=up;i++){
            ssz=0;
            for(int j=0;j<sz;j++)if(i&bin[j])stk[ssz++]=pm[j];
            t=0;
            for(int k=0,uup=(1<<ssz),s,c;k<uup;k++){
                s=1;
                c=0;
                for(int l=0;l<ssz;l++)if(k&bin[l])s*=stk[l],c^=1;
                if(c)t-=m/s;
                else t+=m/s;
            }
            if(ssz&1)Del(re,fast(t,n));
            else Add(re,fast(t,n));
        }
        return re;
    }
    void solve(){
        int ans=0;
        for(int i=l;i<=r;i++)Add(ans,calc(i));
        printf("%d\n",ans);
    }
}P30;
struct IHSU{
    void solve(){
//      if(0);
        if(n<=200&&m<=200)P20.solve();
        else P30.solve();
    }
}P50_1;
void init2(){
    mu[1]=1;
    for(int i=2,tp;i<1005;i++){
        if(vis[i]==0)prime[psz++]=i,mu[i]=-1;
        tp=-mu[i];
        for(int j=0,t;j<psz&&((t=prime[j]*i)<1005);j++){
            vis[t]=1;
            if(i%prime[j]==0){
                mu[t]=0;
                break;
            }
            mu[t]=tp;
        }
    }
}
void solve2(){
    init2();
    while(cas--)scanf("%d %d %d %d",&n,&m,&l,&r),P50_1.solve();
}
int main(){
    freopen("gcdlcm.in","r",stdin);
    freopen("gcdlcm.out","w",stdout);
    scanf("%d %d",&cas,&tp);
    if(tp==1)solve1();
    else solve2();
    return 0;
}

其他

要做的题

知识共享许可协议
本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可。

本文链接:https://hs-blog.axell.top/archives/cxjy-day-4/