第三届信大超越杯团体赛题解

第三届信大超越杯团体赛题解

A 红红找蓝蓝

题解:

宽搜bfs,定义状态{x,y,d,Dir}表示:到(x,y)点拐了d次弯,上一次的方向为Dir

与最短路不同的是,我们从一个点出发要把一个方向上的所有点加入队列,因为这个方向上所有点的拐弯数都只是+1,为了维护先搜到的点拐弯数越少,就要把一个方向上所有点加入队列。

注意不能省略vis[x][y],是为了剪除大量没有意义的搜索,并且如果不加的话会爆空间,别问我为啥知道

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,ax,ay,bx,by;
bool canarrive;
char m[110][110],t;
bool vis[110][110];
int dir[4][2]={0,1,0,-1,1,0,-1,0};
void Read(){
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            t=getchar();
            while(t=='\n'||t==' ')t=getchar();
            m[i][j]=t;
            if(t=='A'){
                ax=i;ay=j;
            }
            if(t=='B'){
                bx=i;by=j;
            }
        }
    }
}
struct node{
    int x,y,d,Dir;//意义为:走到(x,y)拐了d次弯,上一次的方向是Dir
    node(int xx,int yy,int dd,int ddd){
        x=xx;y=yy;d=dd;Dir=ddd;
    }
    node(){}
};
bool check(int x,int y){
    if(x<1||x>n||y<1||y>n||m[x][y]=='x')return false;
    return true;
}
void bfs(){
    memset(vis,0,sizeof vis);
    queue<node> q;
    node u;
    int x,y,d;
    q.push(node(ax,ay,-1,-1));
    vis[ax][ay]=1;
    while(!q.empty()){
        u=q.front();q.pop();
        x=u.x;y=u.y;d=u.d;
        if(x==bx&&y==by){
            printf("%d\n",d);
            canarrive=1;
            return;
        }
        for(int i=0;i<4;++i){
            if(i==u.Dir)continue;
            int vx=x+dir[i][0];
            int vy=y+dir[i][1];
            while(check(vx,vy)){
                if(!vis[vx][vy]){
                    vis[vx][vy]=1;
                    q.push(node(vx,vy,d+1,i));
                }
                vx+=dir[i][0];vy+=dir[i][1];
            }
        }
    }
    printf("-1\n");
}
int main(){
    int T=R();
    while(T--){
        n=R();
        Read();
        bfs();
    }
	return 0;
}

B 红红蓝蓝在争论

​​

题解:

贪心+双指针扫描,我们把题目做两遍,把0变成1,以及把1变成0(下面只描述0变1)

很容易想到我们要变肯定是把相邻的0变成1,这样满足贪心策略

那我们把所有0的下标用vector存起来,l,r指针分别指向m个需要改变的0

那么就可以更新答案为 ans=max(ans,v[r+1]-v[l-1]-1)

l,r指针不断向右++

对于0的个数不足m个以及扫描边界再特殊处理一下就OK了

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,m,ans,s_len;
string s;
vector<int> v[2];
void init(){
    ans=0;
    v[0].clear();
    v[1].clear();
}
void work(int k){
    int v_num=v[k].size();
    if(v_num<=m){
        ans=s_len;
        return;
    }
    int l=0,r;
    for(int i=0;i<v_num;++i){
        if(i+1<=m){
            ans=max(ans,v[k][i+1]);
        }else{
            r=i-1;
            break;
        }
    }
    l++;r++;
    while(r<=v_num-1){
        if(r==v_num-1) ans=max(ans,s_len-v[k][l-1]);
        else ans=max(ans,v[k][r+1]-v[k][l-1]-1);
        l++;r++;
    }
}
int main(){
    int T=R();
    while(T--){
        init();
        n=R();m=R();
        cin>>s;
        s_len=s.size();
        for(int i=0;i<s_len;++i){
            v[s[i]-'0'].push_back(i);
        }
        work(1);
        work(0);
        printf("%d\n",ans);
    }
	return 0;
}

C 合法的 IP 地址

题解:

纯模拟,注意细节判断

给几种不合法的情况:

1..1.1 (这种容易出错)

1.1.1.1.

1x.1.1.1

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
string s;
bool faultchar(char c){
    if(c>='0'&&c<='9'||c=='.')return false;
    return true;
}
bool work(){
    int s_len=s.size();
    char c;
    int num_cnt=0;
    int a[5];
    memset(a,-1,sizeof a);
    for(int i=0;i<s_len;++i){
        if(faultchar(s[i]))return false;
        if(s[i]>='0'&&s[i]<='9'){
            if(a[num_cnt]==-1)a[num_cnt]=0;
            a[num_cnt]=a[num_cnt]*10+s[i]-'0';
        }else{
            num_cnt++;
        }
    }
    if(num_cnt!=3)return false;
    if(a[0]<=0||a[0]>255)return false;
    for(int i=1;i<4;++i){
        if(a[i]<0||a[i]>255)return false;
    }
    return true;
}
int main(){
    int T=R();
    while(T--){
        cin>>s;
        if(work()){
            printf("YES\n");
        }else{
            printf("NO\n");
        }
    }
	return 0;
}

D 石头剪刀布

题解:

真·签到题

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int main(){
    int T=R();
    while(T--){
        int n=R()%3;
        switch(n){
            case 0: printf("Scissors\n");break;
            case 1: printf("Paper\n");break;
            case 2: printf("Rock\n");break;
        }
    }
	return 0;
}

E 填充游戏

题解:

比赛的时候把头都扣烂了没写出来,结果发现代码只有10

如果第一个⚪放不下,那么后手胜,如果第一个能放下,第一个放最中间,后面每次都放在与后手中心对称的位置,则先手必胜。

code:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int T=R();
    while(T--){
        int m=R(),n=R();
        if(m<=2*n) puts("lanlan yyds");
        else puts("honghong yyds");
    }
    return 0;
}

F RickLee And 540

题解:

总情况数:n^{km}

至多选到 i 道的情况数:\binom{i}{n}i^{km}

由容斥定理:

刚好选到n道的情况数为:至多选n道-至多选n-1道+至多选n-2道......

所以: ans = \sum_{i=0}

注意:

  1. 取模一定要充分,可能有一个地方忘了取模就会全wa,如果乘法不放心的话就用__int128过渡一下。
  2. 阶乘的逆元可以在求阶乘的时候顺便求了,当然 inv(fac[i]) 理论上是没错的,因为全都是乘法和加法。
  3. 由于指数km较大,可以使用欧拉降幂公式:a

当p为质数,\Phi (p)=p- 1

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
ll fac[100010],invfac[100010];
ll ksm(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
ll inv(ll x){
    return ksm(x,mod-2);
}
ll C(ll n,ll m){
    return fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void init(){
    fac[0]=invfac[0]=1;
    for(ll i=1;i<=100001;++i){
        fac[i]=fac[i-1]*i%mod;
        invfac[i]=invfac[i-1]*inv(i)%mod;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    init();
    ll T;
    cin>>T;
    while(T--){
        ll n,m,k;
        cin>>n>>m>>k;
        ll fuyi_n=1,ans=0,inv_n=inv(n);
        for(ll i=n;i>=0;--i){
            ans=(ans+fuyi_n*C(n,i)*ksm(i*inv_n%mod,k*m%(mod-1))%mod+mod)%mod; //用到欧拉降幂公式
            fuyi_n*=-1;
        }
        cout<<ans%mod<<'\n';
    }
    return 0;
}

G Jump

题解:

线段树+递推,对于每个位置先求出到达它的最大经验值,再把这个值加到它所能到达的区间的线段树里去,然后对于后面的位置,单点求线段树最大值再加上自己的经验值即为 f[i] 。

线段树区间修改,单点询问。注意线段树空间要开4*n

code:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
struct Tree{
	ll l,r,d;
}o[200010*4];
ll n,m,ans,a[200010];
void build(ll i,ll L,ll R){
	o[i].l=L;o[i].r=R;
	o[i].d=-0x3f3f3f3f;//附加性质初始化
	if(L==R)return;
	ll mid=(L+R)>>1;
	build(i<<1,L,mid);
	build(i<<1|1,mid+1,R);
}
void Add2(ll i,ll L,ll R,ll d){
	if(R<o[i].l||L>o[i].r)return;
	if(L<=o[i].l&&o[i].r<=R){
		o[i].d=max(o[i].d,d);return; 
	}
	Add2(i<<1,L,R,d);
	Add2(i<<1|1,L,R,d);
}
void Ask2(ll i,ll x){
	if(x<o[i].l||x>o[i].r)return;
	if(x>=o[i].l&&x<=o[i].r){
		ans=max(ans,o[i].d);
	}
	if(o[i].l==o[i].r)return;
	Ask2(i<<1,x);
	Ask2(i<<1|1,x);
}
int main(){
	n=R();
	build(1,1,n);
	for(ll i=1;i<=n;++i)a[i]=R();
	ll l,r;
	for(ll i=1;i<=n-1;++i){
		l=R();r=R();
		ans=-0x3f3f3f3f;
		Add2(1,l,r,a[i]);
		Ask2(1,i+1);
		a[i+1]=ans+a[i+1];
	}
	printf("%lld",a[n]);
	return 0;
}

H 信大学子要过河

题解:

贪心,先将a[n]从小到大排序,考虑对于当前时刻想要将最大的两个数渡河,有两种最优策略:

  1. 用最小数分别将两个最大数送过去 cost:a[1]*2+a[i]+a[i-1]
  2. 先让最小和次小数过河,让最小回来,再让两个最大数一起过河,让次小回来:

cost:a[1]+a[2]*2+a[i]

每次比较一下取最小值,在注意一下特殊情况和边界处理就ok了

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,a[100010],ans;
void init(){
    ans=0;
}
void work(){
    if(n==1){
        ans=a[1];
        return;
    }
    for(int i=n;i-1>2;i-=2){
        if(a[1]*2+a[i]+a[i-1]<a[1]+a[2]*2+a[i]){
            ans+=a[1]*2+a[i]+a[i-1];
        }else{
            ans+=a[1]+a[2]*2+a[i];
        }
    }
    if(n&1){ // 奇数
        ans+=a[1]+a[2]+a[3];
    }else{
        ans+=a[2];
    }
}
int main(){
	int T=R();
    while(T--){
        init();
        n=R();
        for(int i=1;i<=n;++i){
            a[i]=R();
        }
        sort(a+1,a+1+n);
        work();
        printf("%d\n",ans);
    }
	return 0;
}

I 中国国粹

题解:

模拟的神!好家伙,我下来调试了4个小时终于A了,我要哭了。。。有个小细节当时改的时候没考虑周全,调试了好久才发现这个问题。

感觉整个人都通透了

教训:仔细看题!!写代码细心!细心!再细心!

顺子型搭子不能考虑字牌!!!

屁胡的判断用枚举对子然后深搜4个搭子

不过好好把这道题写过,你的编程能力会有非常大的提高

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int a[20],b[20],pos,val_count[50],ans[200],ans_cnt,val_a_cnt[50];
void init(){
    memset(val_a_cnt,0,sizeof val_a_cnt);
    memset(ans,0,sizeof ans);
    ans_cnt=0;
    memset(a,0,sizeof a);
    memset(b,0,sizeof b);
}
bool pd1(){
    for(int i=1;i<14;i+=2){
        if(b[i]!=b[i+1])return false;
    }
    return true;
}
bool pd2(){
    int num131[]={0,11,19,21,29,31,39,41,42,43,44,45,46,47},cnt131=13;
    int vis[50]={0};
    for(int i=1;i<=cnt131;++i){
        vis[num131[i]]=1;
    }
    for(int i=1;i<=14;++i){
        if(!vis[b[i]])return false;
        vis[b[i]]++;
    }
    int dui=0;
    for(int i=1;i<=cnt131;++i){
        int x=num131[i];
        if(vis[x]==1)return false; //有的数没有
        if(vis[x]==3){ //有对数
            dui++;
        }
    }
    if(dui>1)return false;
    return true;
}
bool pd3(){
    int jiang[]={0,12,15,18,22,25,28,32,35,38},jiang_num=9;
    bool vis[50]={0};
    for(int i=1;i<=jiang_num;++i){
        vis[jiang[i]]=1;
    }
    for(int i=1;i<=14;++i){
        if(!vis[b[i]])return false;
    }
    return true;
}
bool dfs(int s){
    if(s==4){
        for(int i=1;i<=4;++i){
            for(int j=1;j<=(i==4?7:9);++j){
                int x=i*10+j;
                if(val_count[x])return false;
            }
        }
        return true;
    }
    for(int i=1;i<=4;++i){
        for(int j=1;j<=(i==4?7:9);++j){
            int x=i*10+j;//枚举
            if(!val_count[x])continue;
            if(val_count[x]>=3){
                val_count[x]-=3;
                if(dfs(s+1))return true;
                val_count[x]+=3;
            }
            if(i!=4&&j<=7&&val_count[x+1]&&val_count[x+2]){
                val_count[x]--;
                val_count[x+1]--;
                val_count[x+2]--;
                if(dfs(s+1))return true;
                val_count[x]++;
                val_count[x+1]++;
                val_count[x+2]++;
            }
        }
    }
    return false;
}
bool pd4(){
    for(int i=1;i<=4;++i){
        for(int j=1;j<=(i==4?7:9);++j){
            int x=i*10+j;//枚举
            if(val_count[x]>=2){
                val_count[x]-=2;
                if(dfs(0))return true;
                val_count[x]+=2;
            }
        }
    }
    return false;
}
void print(int x){
    char c;
    switch(x/10){
        case 1:c='m';break;
        case 2:c='s';break;
        case 3:c='p';break;
        case 4:c='c';
    }
    printf("%d%c ",x%10,c);
}
void work(){
    for(int i=1;i<=13;++i)val_a_cnt[a[i]]++;
    for(int i=1;i<=4;++i){
        for(int j=1;j<=(i==4?7:9);++j){
            int x=i*10+j;
            if(val_a_cnt[x]==4)continue;
            bool bj=1;
            pos=0;
            for(int k=1;k<=13;++k){ //插入排序
                if(bj&&x<a[k]){
                    b[++pos]=x;
                    bj=0;
                }
                b[++pos]=a[k];
            }
            if(bj)b[++pos]=x;
            memset(val_count,0,sizeof val_count);
            for(int i=1;i<=14;++i)val_count[b[i]]++;
            if(pd1()||pd2()||pd3()||pd4()){
                ans[++ans_cnt]=x;
            }
        }
    }
}
void out(){
    if(ans_cnt!=0){
        printf("%d ",ans_cnt);
        sort(ans+1,ans+ans_cnt+1);
        for(int i=1;i<=ans_cnt;++i){
            print(ans[i]);
        }
        printf("\n");
    }else{
       printf("Ohno\n"); 
    }
}
int main(){
    int T=R();
    while(T--){
        init();
        int number;char c;
        for(int i=1;i<=13;++i){
            scanf("%d%c",&number,&c);
            switch(c){
                case 'm':a[i]=10+number;break;
                case 's':a[i]=20+number;break;
                case 'p':a[i]=30+number;break;
                case 'c':a[i]=40+number;
            }
        }
        sort(a+1,a+14);
        work();
        out();
    }
	return 0;
}

K 最小整数问题

题解:

真·水题

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
string s;
int main(){
    int T=R();
    while(T--){
        cin>>s;
        sort(s.begin(),s.end());
        int x=0;
        while(s[x]=='0')x++;
        swap(s[0],s[x]);
        cout<<s<<'\n';
    }
	return 0;
}

J Kabayashi And Three consciousness

题解:

模拟+gcd

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
ll p,q;
ll cnt,ans[1000100];
ll gcd(ll a,ll b){
    return (!b?a:gcd(b,a%b));
}
int main(){
	p=R();q=R();
    ll g=gcd(p,q);
    p/=g;q/=g;
    swap(p,q);
    do{
        swap(p,q);
        ll zhengshu=p/q;
        ans[++cnt]=zhengshu;
        p-=q*zhengshu;
        g=gcd(p,q);
        p/=g;q/=g;
    }while(p!=1);
    ans[++cnt]=q;
    printf("%lld\n",cnt);
    for(ll i=1;i<=cnt;++i){
        printf("%lld ",ans[i]);
    }
	return 0;
}

L Find And Cal

题解:

考大素数判断,由于素数的密度较大,可以直接从x++来找素数,用Miller Rabin判断是否为素数。

那么如何求 x! 包含质因数y的个数?

f(x,y)=f(x/y,y)+x/y

推导:

code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
const ll Mod=998244353;
ll prime[10]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ll ksm(ll a,ll b,ll mod){
    ll ans=1;
    while(b){
        if(b&1) ans=(__int128)ans*a%mod;
        a=(__int128)a*a%mod;
        b>>=1;
    }
    return ans;
}
bool miller_rabin(ll x){ //判断素数 
    ll s=0,t=x-1;
    if(x==2)return true; //2是素数 
    if(x<2||!(x&1)) return false; //如果x是偶数或者是0,1,那它不是素数 
    while(!(t&1)){  //将x分解成(2^s)*t的样子 
        s++;
        t>>=1;
    }
    for(ll i=0;i<10&&prime[i]<x;++i){  //随便选一个素数进行测试 
        ll a=prime[i];
        ll b=ksm(a,t,x); //先算出a^t
        for(int j=1;j<=s;++j){ //然后进行s次平方 
            ll k=ksm(b,2,x); //求b的平方 
            if(k==1&&b!=1&&b!=x-1)return false; //用二次探测判断 
            b=k;
        }
        if(b!=1)return false; //用费马小定律判断 
    }
    return true; //如果进行多次测试都是对的,那么x就极有可能是素数 
}
int main(){
    ll T=R();
    while(T--){
        ll x=R(),y=R();
        x++;
        while(!miller_rabin(x))x++;
        ll ans=0;
        while(x){
            ans=(ans+x/y)%Mod;
            x/=y;
        }
        printf("%lld\n",ans);
    }
	return 0;
}
posted @ 2024-03-27 09:00  SaiIing  阅读(9)  评论(0编辑  收藏  举报