Codeforces Round #643 (Div. 2) 题解

A

猜一手不会经过很多次就会有\(Min=0\),然后写个暴力就好了。

ll calc(ll n,ll k)
{
    ll ans=n;
    while (k--)
    {
        int mn=10,mx=0;ll tmp=ans;
        while (tmp)
        {
            int x=tmp%10;tmp/=10;
            mn=min(mn,x);mx=max(mx,x);
        }
        if (!mn) return ans;
        else ans+=mn*mx;
    }
    return ans;
}

int main()
{
    int T=read();
    while (T--)
    {
        ll n=readll(),k=readll();
        printf("%lld\n",calc(n,k-1));
    }
    return 0;
}

B

将序列从小到大排序之后能分组就分组。

int n,a[300300];

int main()
{
    int T=read();
    while (T--)
    {
        n=read();
        rep(i,1,n) a[i]=read();
        sort(a+1,a+1+n);
        int ans=0,now=0;
        rep(i,1,n)
        {
            now++;
            if (now>=a[i]) 
            {
                now=0;ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

C

枚举\(x\),之后对于每个\(y\),合法的\(z\)都是区间\([C,min(x+y-1,D)]\)中的数。

考虑先求出这个右边界的取值范围,再加上使\(z\in[C,D]\)的区间的贡献即可。

int calc(int x,int y,int zl,int zr)
{
    int nowl=zl,nowr=x+y-1;
    int ans=nowr-nowl+1;
    ans=max(0,ans);ans=min(ans,zr-zl+1);
    return ans;
}

int main()
{
    int a=read(),b=read(),c=read(),d=read();
    ll ans=0;
    rep(i,a,b)
    {
        int x=i;
        int L=calc(x,b,c,d),R=calc(x,c,c,d);
        ans+=1ll*(R-L+1)*(L+R)/2;
        int tmp=d-x+1;
        if (tmp>=c) continue;
        tmp=max(b,tmp);
        if (tmp+1<=c)
        {
            ans+=1ll*(d-c+1)*(c-tmp);
        }
    }
    printf("%lld\n",ans);
}

D

构造序列\(1,1,\cdots ,1,m-(n-1)\).之后询问的\(k=m-n\),正确性显然。

int main()
{
    int n=read(),m=read();
    if (m<n*2) {puts("NO");return 0;}
    puts("YES");
    rep(i,1,n-1) printf("1 ");
    printf("%d\n",m-(n-1));
    printf("%d\n",m-(n-1)-1);
}

E

介绍一个猎奇做法。

先只考虑add/remove操作,我们可以枚举终止状态时的高度\(H\in[h_i,h_{i+1}]\),那么\(H\)\(+1\)\(-1\)都只会引起两种操作的固定数量的操作次数发生变化,于是最优花费只会在区间的两个端点取到。

再考虑move操作,注意到如果使用了这一操作就会一直使用到底,也就是最终add/remove操作只会剩下至多一个,我们可以二分一个位置\(h'\),使得若\(H\leq h'\)则remove操作的次数\(\geq\)add操作的次数。也就是此时只会有move和remove操作,变成了最开始的情况。\(H>h'\)的也是类似的。

然后被三分哥按在地上摩擦

#define int long long

int n,a[100100];
ll A,R,M,s[100100];

ll calc(int goal,int p)
{
    ll ca=1ll*goal*p-s[p],cr=s[n]-s[p]-1ll*(n-p)*goal;
    ll ans=ca*A+cr*R;
    ll cm=min(ca,cr);
    ans=min(ans,max(0ll,(cr-cm)*R)+max(0ll,(ca-cm))*A+cm*M);
    return ans;
}

ll solve(int vL,int vR,int p)
{
    ll ans=min(calc(vL,p),calc(vR,p));
    int l=vL,r=vR,pos=vL;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        ll cr=s[n]-s[p]-1ll*mid*(n-p),ca=1ll*p*mid-s[p];
        if (cr>=ca) {pos=mid;l=mid+1;}
        else r=mid-1;
    }
    rep(i,-3,3)
        if ((pos+i>=vL) && (pos+i<=vR)) ans=min(ans,calc(pos+i,p));
    return ans;
}


signed main()
{
    n=read();A=read();R=read();M=read();
    rep(i,1,n) a[i]=read();
    sort(a+1,a+1+n);
    rep(i,1,n) s[i]=s[i-1]+a[i];
    ll ans=4e18;
    rep(i,1,n-1) ans=min(ans,solve(a[i],a[i+1],i));
    printf("%lld\n",ans);
    return 0;
}

F

神必题

最naive的想法是按照每个质数去找,也就是每次询问一个\(p^q\),显然gg。

稍微思考一下发现只要找\(\sqrt n\)的质数,并且可以将一些质数乘在一起询问,再在最后确定每个质数的次幂。但是貌似完成前一步就需要\(22\)次询问,依然不行。

继续往下考虑,注意到\(N\)至多只会有\(9\)个不同的质因子,我们可以留\(5\)次询问来处理次幂。剩下的\(17\)次询问可以全部用来处理小的质因数,笔者的写法可以处理\(<683\)的质因子。

那么对于比它还大的呢?此时若这个数有\(\leq 2\)个这样的质因数,我们可以直接将答案\(\times 2\),而对于\(3\)个的情况,可以分情况讨论:

  • 不再含有其它的质因子,那么最终答案\(=8\),可以直接\(+7\).

  • 含有其它的质因子,那么这个质因子只能为\(2\),也就是最终答案\(=16=2+7+7\),我们仍然可以将之前得到的答案\(+7\).

综上所述,记处理小质因数得到的答案为\(x\),则返回\(\max(2x,x+7)\).

int tot,pri[2002000];
ll maxd=1e18,maxd1=1e9;
bool nopri[2002000];
     
ll ask(ll x)
{
    printf("? %lld\n",x);fflush(stdout);
    ll y;scanf("%lld",&y);
    return y;
}
     
void sieve(int n)
{
    rep(i,2,n)
    {
        if (!nopri[i]) pri[++tot]=i;
        for (int j=1;j<=tot && i*pri[j]<=n;j++)
        {
            nopri[i*pri[j]]=1;
            if (i%pri[j]==0) continue;
        }
    }
}
     
vi d;
int ok=1;
     
void solve()
{
    d.clear();
    int used=0;ll prod=1;
    int lst=1;
    rep(i,1,tot)
    {
        if (maxd/pri[i]<prod)
        {
            ll now=ask(prod);used++;prod=1;
            assert(now>0);
            if (now!=1)
            {
                ll tmp=now;
                for (int j=lst;1ll*pri[j]*pri[j]<=tmp && j<i;j++)
                {
                    if (tmp%pri[j]) continue;
                    while (tmp%pri[j]==0) tmp/=pri[j];
                    d.pb(pri[j]);
                }
                if (tmp!=1) d.pb(tmp);
            }
            if (used==17) break;lst=i;
        }
        prod*=pri[i];
    }
    int len=d.size();int ans=1;
    if (len&1) d.pb(1);
    for (int i=0;i<len;i+=2)
    {
        ll now=1;
        int d1=d[i],d2=d[i+1];
        while (maxd1/now>=d1) now*=d1;
        if (d2!=1)
            while (maxd/now>=d2) now*=d2;
        ll tmp=ask(now);used++;
        int c=0;
        while (tmp%d1==0)
        {
            tmp/=d1;c++;
        }
        ans*=(c+1);
        c=0;
        if (d2==1) continue;
        while (tmp%d2==0)
        {
            tmp/=d2;c++;
        }
        ans*=(c+1);
    }
    ans=max(ans+7,ans*2);
    printf("! %d\n",ans);fflush(stdout);
}
     
int main()
{
    sieve(1000);
    int T=read();
    while (T--) solve();
    return 0;
}
posted @ 2020-05-17 00:59  EncodeTalker  阅读(235)  评论(0编辑  收藏  举报