河北工业大学 ACM 集训队 2023 年夏季选拔 题解 12/12

https://ac.nowcoder.com/acm/contest/59007

A

假设数字n有len位

则小len的长度,每个都有九个方案。

长度和len一样的,至少有n[0]-1种方案

n[0]n[0]n[0]...的这个方案暴力地跑一遍看看是不是小于等于n即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
char s[1000];
int len,ans;
int main()
{
    // freopen("1.in","r",stdin);
    scanf("%s",s+1);
    len=strlen(s+1);
    ans=len*9-9;
    ans++;
    for(int i=2;i<=len;i++)
        if(s[i]<s[1])
        {
            ans--;
            break;
        }
    ans+=s[1]-'1';
    cout<<ans;
}
A

B

 赛场上找规律找出来的,发现是n*n*k的因子数量-1。

考虑把n和k质因子分解,答案是∏(因子数量+1)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read()
{
    ll x;scanf("%lld",&x);return x;
}
ll n,k,ans;
map<int,int>o;
void work()
{
    n=read();k=read();ans=1;o.clear();
    for(int i=2;i*i<=n;i++)
        while(n%i==0)
        {
            o[i]+=2;
            n=n/i;
        }
    if(n!=1)
        o[n]+=2;
    for(int i=2;i*i<=k;i++)
        while(k%i==0)
        {
            o[i]++;
            k=k/i;
        }
    if(k!=1)
        o[k]++;
    for(auto x:o)
        ans=ans*(x.second+1);
    printf("%lld\n",ans-1);
}
int main()
{
    for(ll t=read();t;t--)
        work();
}
B

赛后请数论大师@chdy做了做

 C

输出RUSHB,连我个圈外人都知道

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    cout<<"RUSHB";
}
C

D

注意到n只有8,所以暴力跑n的排列,按题意模拟即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
double ans,sum;
int w0,w[110],x[110],y[110],noww,n,a[110];
double askdis(int u,int v)
{
    return sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v]));
}
void dfs(int d)
{
    if(d==n+1)
    {
        sum=0;noww=w0;
        for(int i=1;i<=n;i++)
        {
            sum=sum+askdis(a[i],a[i-1])*noww;
            noww=noww+w[a[i]];
        }
        ans=min(ans,sum);
        return ;
    }
    for(int i=d;i<=n;i++)
    {
        swap(a[i],a[d]);
        dfs(d+1);
        swap(a[i],a[d]);
    }
}
int main()
{
    ans=1e18;
    n=read();w0=read();
    for(int i=1;i<=n;i++)
        x[i]=read();
    for(int i=1;i<=n;i++)
        y[i]=read();
    for(int i=1;i<=n;i++)
    {
        w[i]=read();
        a[i]=i;
    }
    dfs(1);
    printf("%.7lf",ans);
}
D

E

 注意到k只有50,m只有1000,所以mk地模拟一下即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
ll a[1010];
int k,m,c[60],mod=998244353;
int main()
{
    k=read();m=read();
    for(int i=1;i<=k;i++)
        c[i]=read();
    for(int i=1;i<=k;i++)
        a[i]=read();
    for(int i=k+1;i<=m;i++)
        for(int j=1;j<=k;j++)
            a[i]=(a[i]+c[j]*a[i-j])%mod;
    cout<<a[m];
}
E

F

经典前缀和。考虑把+看成1,-看成-1,区间[l,r]“正负相互抵消”等价于l-1和r的前缀和相等。

所以记录一下每个前缀和第一次出现的位置,ans=max(每个前缀和最后一次出现的位置-第一次出现的位置)

代码方面可以用+1000000的方式把负数前缀和转移到正数里,然后用数组存。或者像我一样使用map存第一次出现的位置。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
char s[1000010];
map<int,int>f;
int sum,n,ans;
int main()
{
    f[0]=0;
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='+')
            sum++;
        else
            sum--;
        if(f.count(sum))
            ans=max(ans,i-f[sum]);
        else
            f[sum]=i;
    }
    cout<<ans;
}
F

G

问题是说给定一个对称串,能否重新排列使得变成另一个对称串。

那么对称的两个字母必须一起行动,如果n是奇数,中间的字母不影响答案。

所以std统计一下对称的字母的种类数,如果等于1就不能,对应着aaaaaa或者aabaa这些情况。

如果大于1就可以,此时把任意两个不相同的字母交换一下即可。

总之,std是对的,输出yes(话说我去cf交一下代码不就知道对不对了)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int t=read();t;t--)
        cout<<"YES\n";
}
G

H

出题人生怕不会写,甚至把判断凸包包含给端上来了,并且点的坐标都是整数。

考虑暴力枚举i,j,判断如果一个在另一个里面就连有向边,于是会连出来一个有向无环图。

对有向无环图跑拓扑排序,计算深度d即可。将d排个序输出。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
struct tnode
{
    ll x,y;
};
vector<tnode>node[110];
int n;
int in(int a,int b)
{
    ll x0=node[a][0].x;
    ll y0=node[a][0].y;
    ll x1,y1,x2,y2;
    for(int i=1;i<node[b].size();i++)
    {
        x1=node[b][i-1].x;y1=node[b][i-1].y;
        x2=node[b][i].x;y2=node[b][i].y;
        if((x1-x0)*(y2-y0)<=(x2-x0)*(y1-y0))
            return 0;
    }
    x1=node[b][node[b].size()-1].x;y1=node[b][node[b].size()-1].y;
    x2=node[b][0].x;y2=node[b][0].y;
    if((x1-x0)*(y2-y0)<=(x2-x0)*(y1-y0))
        return 0;
    return 1;
}
vector<int>e[110];
queue<int>q;
int d[110],ru[110];
priority_queue<int>ans;
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        for(int k=read();k;k--)
        {
            int x=read();
            node[i].push_back({x,read()});
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==j)continue;
            if(in(j,i))//
            {
                ru[j]++;
                e[i].push_back(j);
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(ru[i]==0)
        {
            d[i]=1;
            q.push(i);
        }
    while(q.size())
    {
        int x=q.front();
        q.pop();
        for(auto y:e[x])
        {
            ru[y]--;
            if(ru[y]==0)
            {
                d[y]=d[x]+1;
                q.push(y);
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(!e[i].size())
            ans.push(-d[i]);
    while(ans.size())
    {
        printf("%d ",-ans.top());
        ans.pop();
    }
}
H

I

计数类问题。考虑数字x在第i位的方案对答案的贡献是pow(10,i-1)*(n-1)! *x,计算一下即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
ll n,sum,now,t,mod=1e9+7;
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
    {
        sum=sum+read();
        t=(t*10+1)%mod;
    }
    now=1;
    for(int i=1;i<n;i++)
        now=now*i%mod;
    cout<<t*sum%mod*now%mod;
}
I

J

大胆模拟,注意数组要开ll,否则1e8*32会爆掉。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read()
{
    ll x;scanf("%lld",&x);return x;
}
ll n,m,a[210][210];
struct node
{
    ll a,b;
    char c;
}o[210][210];
char s[100];
int main()
{
//     freopen("1.in","r",stdin);
    n=read();m=read();
    if(read()&1)
    {
        scanf("%s",s);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%lld%c%lldi",&o[i][j].a,&o[i][j].c,&o[i][j].b);
                printf("%lld%c%lldi ",o[i][j].a,o[i][j].c,o[i][j].b);
            }
            printf("\n");
        }
        printf("%ss have\n",s);
        if(s[4]=='y')
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    o[i][j].a*=8,o[i][j].b*=8;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld%c%lldi ",o[i][j].a,o[i][j].c,o[i][j].b);
            printf("\n");
        }
        printf("mouths,\n");

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld%c%lldi ",o[i][j].a*2,o[i][j].c,o[i][j].b*2);
            printf("\n");
        }
        printf("eyes and\n");

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld%c%lldi ",o[i][j].a*4,o[i][j].c,o[i][j].b*4);
            printf("\n");
        }
        printf("legs.");
    }
    else
    {
        scanf("%s",s);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%lld",&a[i][j]);
                printf("%lld ",a[i][j]);
            }            
            printf("\n");
        }
        printf("%ss have\n",s);

        if(s[4]=='y')
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    a[i][j]*=8;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld ",a[i][j]);
            printf("\n");
        }
        printf("mouths,\n");

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld ",a[i][j]*2);
            printf("\n");
        }
        printf("eyes and\n");

        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                printf("%lld ",a[i][j]*4);
            printf("\n");
        }
        printf("legs.");
    }
}
J

K

注意到ai最多有log(ai)个1,所以  以i位结尾的所有区间的与最多有log(ai)种不同的数,用set维护一下以i位结尾的区间的与。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
set<int>ans[200010];
int main()
{
    int n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        ans[i].insert(x);
        for(auto y:ans[i-1])
            ans[i].insert(x&y);
        printf("%d ",ans[i].size());
    }
}
K

L

大胆dp

f[i][j][kk]表示在i行第j列花费了kk活力值能获得的最大的祝福值。

f[i][j][kk]=max(f[i-1][j][kk],f[i][j-1][kk]);

若kk>=a[s[i][j]-'0'],可以更新

f[i][j][kk]=max(f[i][j][kk],max(f[i][j-1][kk-val]+a[val],f[i-1][j][kk-val]+a[val]));

最后输出f[n][m][k]

posted @ 2023-05-26 21:53  zzuqy  阅读(199)  评论(0编辑  收藏  举报