筑梯杯郑州轻工业大学第十七届程序设计大赛题解(12/12)

比赛链接:https://ac.nowcoder.com/acm/contest/106899

我是捧杯的梦想 QY2002

ANo idea

队友写的,模拟题,遇到2024修改一下即可

#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
int T,n;
char s[maxn];
int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    scanf("%d",&T);
    while(T--){
        scanf("%s",s+1);n=strlen(s+1);
        for(int i=1;i<=n;i++){
            if(s[i]=='2'&&s[i+1]=='0'&&s[i+2]=='2'&&s[i+3]=='4')s[i+3]++,i+=3;
        }
        for(int i=1;i<=n;i++)printf("%c",s[i]),s[i]=0;
        printf("\n");
    }
    return 0;
}
A

B超椭圆

队友写的

这题推式子非常难,但是我们打的网络赛,队友百度了一下式子,代入即可

#include<bits/stdc++.h>
using namespace std;
double solve(double a,double b,double n){
    if(n<=0)return 0.0;
    double a1=std::tgamma(1.0+1.0/n);
    double a2=std::tgamma(1.0+2.0/n);
    double area=4.0*a*b*(a1*a1)/a2;
    return area;
}
int main(){
    double a,b,n;
    scanf("%lf%lf%lf",&a,&b,&n);
    printf("%.7lf\n",solve(a,b,n));
    return 0;
}
B

正经去写应该微元法做积分

C简单题

使用了类似字典树和基数排序的思想

考虑第一位选某个值有多少个,比如1 2 3 ,第一位选1的有三个,第一位选2的有两个,第一位选3的有一个。看k的大小可以得知第一位的值,接下来继续看第二位,接下来继续看第三位。直到k超过了全体的方案数,说明这一位留空,开始输出即可。

例如k=6,当确定第一位选1后,问题转变成了123,12,1里面选第3大。在第一位选1的三种方案里,有2种方案是第二位为2,1种方案是第二位为空,发现k>2,因此可以确定第二位为空,输出1即可结束。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5010,mod=998244353;//1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}
int lowbit(int x){return x&(-x);}
int n,k,a[N];
vector<int>b;
struct node
{
    int l,r,len;
};
int sum[N];
vector<int>o[2];
int main()
{
    n=read();k=read();
    for(int i=1;i<=n;i++)
    {
         a[i]=read();
         b.push_back(a[i]);
         o[0].push_back(i);
    }    
    sort(b.begin(),b.end());
    b.erase(unique(b.begin(),b.end()),b.end());
     for(int i=1;i<=n;i++)
        a[i]=lower_bound(b.begin(),b.end(),a[i])-b.begin()+1;
    for(int ans=0;;ans++)
    {
        memset(sum,0,sizeof(sum));
        for(auto l:o[ans%2])
        {
            int r=l+ans;
            if(r<=n)
                sum[a[r]]+=n-l+1-ans;
            else
                sum[n+1]+=1;
        }
        int pos=0;
        for(int i=n;i>=1;i--)
        {
            if(sum[i]>=k)
            {
                pos=i;
                break;//答案在这里
            }
            k-=sum[i];
        }
        
        if(pos==0)
        {
            for(auto l:o[ans%2])
            {
                for(int i=l;i<l+ans;i++)
                    cout<<b[a[i]-1]<<' ';
                exit(0);
            }
        }
        int nxt=1-ans%2;
        o[nxt].clear();
        for(auto l:o[ans%2])
        {
            int r=l+ans;
            if(a[r]==pos)
            {
                o[nxt].push_back(l);
            }
        }
        
    }
}
C

D双影奇境

队友写的

想要互相到达,首先需要(x+y)%2=(x+y)%2,其次需要在同一个联通块里

其次还有一种特殊情况是不能闪转腾挪的情况:比如一个人旁边就是终点,周围全是障碍,这个时候必须一步到位。

#include<bits/stdc++.h>
#define maxn 510
#define maxm 300010
using namespace std;
int n,m,q,a1,a2,a3,a4,a5,a6,fa[maxm],dis[maxm];
char s[maxn][maxn];
int z(int q1,int q2){return (q1-1)*m+q2;}
void find(int x){
    if(fa[x]==x)return;
    find(fa[x]);
    dis[x]=(dis[x]+dis[fa[x]])%2;
    fa[x]=fa[fa[x]];
    return;
}
void he(int x,int y){
    find(x),find(y);
    int f1=fa[x],f2=fa[y];
    if(f1!=f2)fa[f2]=f1,dis[f2]=dis[y]^dis[x]^1;
    return;
}
int sb1(int x1,int y1,int x2,int y2){
    if(x1==x2&&y1==y2-1)return 1;
    if(x1==x2&&y1==y2+1)return 1;
    if(y1==y2&&x1==x2-1)return 1;
    if(y1==y2&&x1==x2+1)return 1;
    return 0;
}
int sb2(int x1,int y1,int x2,int y2){
    if(x1-1!=x2&&s[x1-1][y1]=='.')return 0;
    if(x1+1!=x2&&s[x1+1][y1]=='.')return 0;
    if(y1-1!=y2&&s[x1][y1-1]=='.')return 0;
    if(y1+1!=y2&&s[x1][y1+1]=='.')return 0;
    return 1; 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            fa[z(i,j)]=z(i,j);
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(s[i][j]=='#')continue;
            if(i!=1&&s[i-1][j]!='#')he(z(i-1,j),z(i,j));
            if(j!=1&&s[i][j-1]!='#')he(z(i,j-1),z(i,j));
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            find(z(i,j)); 
            //cout<<dis[z(i,j)]<<" ";
        }//cout<<endl;
    }
    scanf("%d",&q);
    while(q--){
        scanf("%d%d",&a1,&a2);scanf("%d%d",&a3,&a4);scanf("%d%d",&a5,&a6);
        if(sb1(a1,a2,a5,a6)&&sb1(a3,a4,a5,a6)){printf("Yes\n");continue;}
        if(sb1(a1,a2,a5,a6)&&sb2(a1,a2,a5,a6)){printf("No\n");continue;}
        if(sb1(a3,a4,a5,a6)&&sb2(a3,a4,a5,a6)){printf("No\n");continue;}
        find(z(a1,a2)),find(z(a3,a4)),find(z(a5,a6));
        int f1=fa[z(a1,a2)],f2=fa[z(a3,a4)],f3=fa[z(a5,a6)];
        //cout<<f1<<" "<<dis[z(a1,a2)]<<" "<<dis[z(a3,a4)]<<endl;
        if(f1!=f2||f2!=f3)printf("No\n");
        else{
            if(dis[z(a1,a2)]==dis[z(a3,a4)])printf("Yes\n");
            else printf("No\n");
        }
    }
    return 0;
}
D

E小刻与奇怪队列

队友写的,看起来是个splay?

#include<bits/stdc++.h>
#define maxn 100010
#define inf 2147483647
using namespace std;
int sum;
struct node{int l,r,z,c,si,s;}a[maxn];
int _new(int x){a[++sum].z=x;a[sum].s=rand();a[sum].c=a[sum].si=1;return sum;}
void update(int x){a[x].si=a[a[x].l].si+a[a[x].r].si+a[x].c;return;}
void build(int &root){root=_new(-inf);a[root].r=_new(inf);update(root);return;}
void zig(int &p){int q=a[p].l;a[p].l=a[q].r;a[q].r=p;p=q;update(a[p].r);update(p);return;}
void zag(int &p){int q=a[p].r;a[p].r=a[q].l;a[q].l=p;p=q;update(a[p].l);update(p);return;}
void insert(int &p,int x){if(p==0){p=_new(x);return;}if(a[p].z==x){a[p].c++;update(p);return;}if(x<a[p].z){insert(a[p].l,x);if(a[a[p].l].s>a[p].s)zig(p);}else{insert(a[p].r,x);if(a[a[p].r].s>a[p].s)zag(p);}update(p);return;}
void remove(int &p,int x){if(p==0)return;if(a[p].z==x){if(a[p].c>1)a[p].c--;else{if(a[p].l||a[p].r){if(a[p].r==0&&a[a[p].l].s>=a[a[p].r].s)zig(p),remove(a[p].r,x);else zag(p),remove(a[p].l,x);}else p=0;}update(p);return;}if(x<a[p].z)remove(a[p].l,x);else remove(a[p].r,x);update(p);return;}
int getz(int p,int x){if(a[p].si<x)return inf;if(a[a[p].l].si>=x)return getz(a[p].l,x);if(a[a[p].l].si+a[p].c>=x)return a[p].z;return getz(a[p].r,x-a[a[p].l].si-a[p].c);}
int root,n,a1,a2,ll=1,rr=0,su=0;
map<int,int>mp;
int main(){
    scanf("%d",&n);build(root);
    while(n--){
        scanf("%d",&a1);
        if(a1==1){
            scanf("%d",&a2);
            ll--;mp[ll]=a2;insert(root,ll);su++;
        }
        else if(a1==2){
            scanf("%d",&a2);
            rr++;mp[rr]=a2;insert(root,rr);su++;
        }
        else{
            int fz=0;
            if(su%2==0)fz=su/2;
            else fz=su/2+1;
            fz=getz(root,fz+1);
            printf("%d\n",mp[fz]);
            remove(root,fz);su--;
        }
    }
    return 0;
}
E

F你好多宝宝,你开幼儿园算了

构造题

最好是给最大边替换成0,这样就需要在最大边的外面找俩点连边

设最大边的端点是x和y,由于n>2,因此不可能xy都是叶子,度数都为1。不妨设x的度数大于1,则可以输出y和x的任意一个非y邻接点

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=998244353;//1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}
int lowbit(int x){return x&(-x);}
int n;
struct node
{
    int x,y,w;
    friend bool operator <(node a,node b)
    {
        return a.w<b.w;
    }
}e[N];
set<int>o[N];
void work()
{
    n=read();
    for(int i=1;i<=n;i++)
        o[i].clear();
    for(int i=1;i<n;i++)
    {
        e[i].x=read();
        e[i].y=read();
        e[i].w=read();
        o[e[i].x].insert(e[i].y);
        o[e[i].y].insert(e[i].x);
    
    }
    sort(e+1,e+n);
    if(o[e[n-1].x].size()==1)
        swap(e[n-1].x,e[n-1].y);
    o[e[n-1].x].erase(e[n-1].y);
    cout<<e[n-1].y<<' '<<*o[e[n-1].x].begin()<<' '<<0<<'\n';
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    for(int t=read();t;t--)
        work();
}
F

G数组笑传之查查表

队友写的,好像也写了个splay?

#include<bits/stdc++.h>
#define maxn 100010
#define inf 2147483647
using namespace std;
int sum;
struct node{int l,r,z,c,si,s;}a[maxn];
int _new(int x){a[++sum].z=x;a[sum].s=rand();a[sum].c=a[sum].si=1;return sum;}
void update(int x){a[x].si=a[a[x].l].si+a[a[x].r].si+a[x].c;return;}
void build(int &root){root=_new(-inf);a[root].r=_new(inf);update(root);return;}
void zig(int &p){int q=a[p].l;a[p].l=a[q].r;a[q].r=p;p=q;update(a[p].r);update(p);return;}
void zag(int &p){int q=a[p].r;a[p].r=a[q].l;a[q].l=p;p=q;update(a[p].l);update(p);return;}
void insert(int &p,int x){if(p==0){p=_new(x);return;}if(a[p].z==x){a[p].c++;update(p);return;}if(x<a[p].z){insert(a[p].l,x);if(a[a[p].l].s>a[p].s)zig(p);}else{insert(a[p].r,x);if(a[a[p].r].s>a[p].s)zag(p);}update(p);return;}
int getrank(int p,int x){if(p==0)return 0;if(a[p].z==x)return a[a[p].l].si;if(x<a[p].z)return getrank(a[p].l,x);return a[a[p].l].si+a[p].c+getrank(a[p].r,x);}
int root,n,a1[maxn],a2[maxn];
int main(){
    scanf("%d",&n);build(root);
    for(int i=1;i<=n;i++)scanf("%d",&a1[i]),insert(root,a1[i]);
    for(int i=1;i<=n;i++)scanf("%d",&a2[i]);
    for(int i=1;i<=n;i++){
        int ans=getrank(root,a1[i])-1;
        if(i!=1)printf(" ");
        if(2*a2[i]<=ans)printf("0");
        else printf("%d",a2[i]*2-ans);
    }
    printf("\n");
    return 0;
}
G

H ARK no NIGHTS
模拟题

考虑写一个暴力,得到某个时间点的总毫秒数。则有没有超过8小时只需要看看总毫秒数的差值有没有超过8小时的总毫秒数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=998244353;//1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}
int lowbit(int x){return x&(-x);}
int flag[30];
ll ask(int a,int b,ll c,int d,int e,int f)
{
    ll t=f+e*60+d*60*60;
    for(int i=2000;i<a;i++)
        for(int j=1;j<=12;j++)
            if(flag[j])
                c=c+31;
            else if(j==2)
            {
                if(i%400==0||i%4==0&&i%100!=0)
                    c=c+29;
                else
                    c=c+28;
            }
            else
                c=c+30;
    
    for(int i=a;i<=a;i++)
        for(int j=1;j<b;j++)
            if(flag[j])
                c=c+31;
            else if(j==2)
            {
                if(i%400==0||i%4==0&&i%100!=0)
                    c=c+29;
                else
                    c=c+28;
            }
            else
                c=c+30;
    return c*24*60*60ll+t;
}
void work()
{
    int a,b,c,d,e,f;
    scanf("%d-%d-%dT%d:%d:%d",&a,&b,&c,&d,&e,&f);
    ll tt=ask(a,b,c,d,e,f);
    scanf("%d-%d-%dT%d:%d:%d",&a,&b,&c,&d,&e,&f);
    tt=ask(a,b,c,d,e,f)-tt;
    if(tt>=ask(0,0,0,8,0,0))
        cout<<"Yes\n";
    else
        cout<<"No\n";
}
int main()
{
    flag[1]=flag[3]=flag[5]=flag[7]=flag[8]=flag[10]=flag[12]=1;
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    for(int t=read();t;t--)
        work();
}
H

I #define int bigint

众所周知,a%b=a-a/b*b

因此a/b=(a-a%b)/b

a%b很容易做,是%b意义下的快速幂。得到0~b-1范围内的a%b,再求一下%1e9+7下的a的值,两者相减,再除以b的%1e9+7下的逆元即可得到答案

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}int lowbit(int x){return x&(-x);}
ll quick(ll a,ll b,ll modd)
{
    ll t=1;
    while(b)
    {
        if(b&1)
            t=t*a%modd;
        b=b/2;
        a=a*a%modd;
    }
    return t;
}
ll n,p[N],e[N],b,ans;
ll ask()
{
    ll t=1;
    for(int i=1;i<=n;i++)
    {
        t=t*quick(p[i],e[i])%mod;
    }
    return t;
}

void work()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        p[i]=read();
        e[i]=read();
    }
    b=read();
    ans=1;
    for(int i=1;i<=n;i++){
        ans=ans*quick(p[i],e[i],b)%b;
    }
    ans=(ask()-ans)*quick(b,mod-2)%mod;
    ans=(ans+mod)%mod;
    cout<<ans;
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    // for(int t=read();t;t--)
        work();
}
I

J C 属性大爆发

大模拟

首先可以用dxdy分别表示xy的运动方向,则每次反弹都是相同的dx=-dx,dy=-dy

然后注意细节,多构造测试点调试即可

// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010,mod=998244353;//1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}
int lowbit(int x){return x&(-x);}
int n,m,t;
char s[N][N];
int xx,yy,dx,dy,k;
int main()
{
    n=read();m=read();t=read();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s[i]+1);
        for(int j=1;j<=m;j++)
            if(s[i][j]=='O')
                xx=i,yy=j,s[i][j]='.';                
    }
    for(int j=0;j<=m+1;j++)
        s[0][j]=s[n+1][j]='#';
    for(int i=1;i<=n;i++)
        s[i][0]=s[i][m+1]='.';
    k=read();
    for(int i=1;i<=k;i++)
    {
        char c;
        cin>>c;
        if(c=='U')
            dx-=1;
        else if(c=='R')
            dy+=1;
        else if(c=='D')
            dx+=1;
        else
            dy-=1;
    }
    for(int i=1,num=1;i<=t&&num<=50000000;i++,num++)
    {        
        if(dx&&dy)//45°
        {
            if(s[xx][yy+dy]=='#'||s[xx+dx][yy]=='#')//过不去,需要反弹
            {
                i--;
                if(s[xx][yy+dy]=='#'){
                    dy=-dy;
                    if(s[xx][yy+dy]=='#'){
                        dy=0;
                    }
                }
                if(s[xx+dx][yy]=='#'){
            
                    dx=-dx;
                    if(s[xx+dx][yy]=='#'){
                        dx=0;
            
                    }
                }
            }
            else if(s[xx+dx][yy+dy]=='#')//虽然旁边没有,但是这里也有
            {
                i--;
                dx=-dx;
                dy=-dy;
            }
            else //都没有,过去
            {
                xx+=dx;
                yy+=dy;
            }
            
        }
        else//横着走
        {
            if(s[xx+dx][yy+dy]=='.')
            {
                xx+=dx;
                yy+=dy;
            }
            else
            {
                i--;
                dx=-dx;
                dy=-dy;
            }
        }
        if(yy==0){
            cout<<"Lose";
            exit(0);
        }
        if(yy==m+1)
        {
            cout<<"Win";
            exit(0);
        }
        if(dx==0&&dy==0)
            break;
    }
    cout<<'('<<xx<<", "<<yy<<')';
}
J

K 计算机的末路

状压dp+最短路

共有2k+1个特殊点,先跑21次最短路

接下来状压:f[S][j]表示过特殊点的状态为S,停在了j号特殊点的最短距离

对于每份订单,必须先去取餐点再去送餐点,因此需要在送餐点转移的时候格外注意有没有去过取餐点

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int read(){int x;scanf("%d",&x);return x;}
int n,m,k,p;
int vis[N],nod[N],cnt,s[12],t[12];
ll d[22][N];
vector<pair<int,int>>e[N];
void insert(int x)
{
    // if(pos[x])
        // return ;
    nod[cnt]=x;//rk和编号映射
    // pos[x]=cnt;//编号和rk映射
    cnt++;
}
ll f[1<<21][21];
priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>>q;
void dij(int x,int rk)
{
    for(int i=1;i<=n;i++)
        vis[i]=0;
    d[rk][x]=0;
    q.push({0,x});
    while(q.size())
    {
        x=q.top().second;
        q.pop();
        if(vis[x])
            continue;
        vis[x]=1;
        for(auto [y,v]:e[x])
        {
            if(d[rk][y]>d[rk][x]+v)
            {
                d[rk][y]=d[rk][x]+v;
                q.push({d[rk][y],y});
            }
        }
    }
}
int main()
{
    n=read();m=read();k=read();p=read();
    // n=100000;m=100000;k=10;p=3;
    insert(p);
    memset(d,0x3f,sizeof(d));
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read(),v=read();
        // int x=i,y=i+1,v=3;
        e[x].push_back({y,v});
        e[y].push_back({x,v});
    }
    for(int i=1;i<=k;i++)
    {
        s[i]=read(),t[i]=read();
        // s[i]=rand()%n+1,t[i]=rand()%n+1;
        insert(s[i]);
        insert(t[i]);
    }
    for(int i=1;i<=cnt;i++)
        dij(nod[i],i);
    memset(f,0x3f,sizeof(f));
    f[1][0]=0;
    for(int i=0;i<(1<<cnt);i++)
    {
        for(int x=1;x<cnt;x++)//这次要送x
        {
            if((i&(1<<x))==0)
                continue;
            if(x%2==0&&(i&(1<<(x-1)))==0)//要送这个但是没去过起点
                continue;
            for(int y=0;y<cnt;y++)
            {
                if(i&(1<<y))
                    f[i][x]=min(f[i][x],f[i-(1<<x)][y]+d[x][nod[y]]);
            }
        }
    }
    ll ans=f[(1<<cnt)-1][0];
    for(int i=1;i<cnt;i++)
    {
        ans=min(ans,f[(1<<cnt)-1][i]);
    }
    cout<<ans;
}
K

L 割方术

输出四个输入数字的gcd即可

// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=998244353;//1e9+7;
ll read(){ll x;scanf("%lld",&x);return x;}
ll quick(ll a,ll b){ll t=1;while(b){if(b&1)t=t*a%mod;b=b/2;a=a*a%mod;}return t;}
int lowbit(int x){return x&(-x);}
void work()
{
    ll n=read(),m=read(),N=read(),M=read();
    cout<<__gcd(__gcd(n,m),__gcd(N,M))<<'\n';
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    for(int t=read();t;t--)
        work();
}
L

 

posted @ 2025-04-22 15:46  zzuqy  阅读(45)  评论(0)    收藏  举报