第七届Code+程序设计全国挑战赛 补题

最小路径串

题目描述

n 个点 m 条边的无向图中,所有点用从 0 开始的 6 位数字串编号,即 000000、000001、000002、……直到 n−1 对应的 6 位数字串。保证 n≤106,所以 6 位的编号不会溢出。

对于除了 000000 以外的每个点,你需要找到一条从 000000 出发且不经过重复点的路径,使得路径上所有点的数字串顺次连接形成的串的字典序最小。

比较两个不同的串的字典序的方法是:如果其中某个串是另一个的前缀,则较短的串字典序较小;否则,找出两个串从左往右扫描时遇到的首个不相等的位置,在这个位置上的数字较小的串字典序较小。

由于输出路径过于麻烦,你不需要完整地输出路径,只需要将路径上所有点的数字串视作一个整数,输出这个数对 998244353 取模的结果。

输入格式

从标准输入读入数据。

第一行输入两个整数 n 和 m。

第二行输入一个长度为 12m 的数字串,依次表示每条边。每条边用 12 个数字表示,其中前 6 个与后 6 个数字分别表示这条边所连接的两个点的编号。

注意,输入中可能会包含自环或重边。

输出格式

输出到标准输出。

输出 n−1 行,依次输出除了点 000000 本身以外,点 000000 到每个点的字典序最小的路径,视为整数后对 998244353 取模的结果。

如果点 000000 不可到达某个点,则在对应的行改为输出 -1。

样例1输入
5 5
000000000003000001000003000001000002000002000000000002000003
样例1输出
2000001
2
517560944
-1
样例1解释
从 000000 到 000001 所求的路径对应的串为 000000000002000001。
从 000000 到 000002 所求的路径对应的串为 000000000002。
从 000000 到 000003 所求的路径对应的串为 000000000002000001000003,对 998244353 取模后为 517560944。
从 000000 到 000004 不存在路径。

子任务

子任务1(11分)
1≤n≤1e6,m=0。

子任务2(55分)
1≤n≤10,0≤m≤20。

子任务3(34分)
1≤n≤1e6,0≤m≤1e6。

题目思路

比赛写了一个暴力66分,问了一下学长,其实很简单,就是简单贪心就行了。dfs时优先走编号小的点,走到一个点就可以算出这个点的答案。

代码

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=1e6+5,mod=998244353;
int n,m,head[maxn],cnt,ans;
ll len[maxn];
bool vis[maxn];
char s[24*maxn];
string str;
struct node{
    int to,next;
}e[maxn<<1];
void add(int u,int v){
    e[++cnt].next=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
void dfs(int son,ll dis){
    len[son]=dis;
    set<int> se;
    for(int i=head[son];i;i=e[i].next){
        se.insert(e[i].to);
    }
    for(set<int>::iterator ite=se.begin();ite!=se.end();ite++){
        if(!vis[*ite]){
            vis[*ite]=1;
            dfs(*ite,(dis*1000000+*ite)%mod);
        }
    }
}
signed main(){
    memset(len,-1,sizeof(len));
    scanf("%d %d %s",&n,&m,s+1);
    int d=strlen(s+1);
    for(int i=1;i<=d;i+=12){
        int v=0,u=0;
        for(int j=i;j<=i+11;j++){
            if(j<i+6){
                v=v*10+s[j]-'0';
            }else{
                u=u*10+s[j]-'0';
            }
        }
        add(v,u),add(u,v);
    }
    vis[0]=1;
    dfs(0,0);
    for(int i=1;i<=n-1;i++){
        printf("%lld\n",len[i]);
    }
    return 0;
}


蚂蚁

题目描述

在东西排布的两棵树之间悬挂着一条长为 L 的细绳,有 N 只蚂蚁在这条绳上。这些蚂蚁希望通过绳子爬到任何一棵树上,但这条绳太细了,导致两只蚂蚁不能并排爬行,也不能交错而过。

它们想到了一个方法:每只蚂蚁都以每单位时间移动一个单位距离的速度不断向前爬,当迎面碰到另一只蚂蚁时,两只蚂蚁都将立即掉头并继续向前爬。现在,蚂蚁们想知道自己是否能爬下绳子,如果能,它们还希望知道自己爬下绳子所花的时间。为了方便,我们按初始时位置从东到西的顺序对蚂蚁从 1 开始编号。

输入格式

从标准输入读入数据。

输入的第一行包含两个正整数 N,L,保证 N≤1e5,L≤1e9,且 N<L。

输入的第二行包含 N 个正整数,第 i 个数 pi 表示第 i 只蚂蚁到东侧树木的距离,保证 pi 随 i 增大严格递增,且 0<pi<L。

输入的第三行包含 N 个整数,第 i 个数 di 若为 1 则表示第 i 只蚂蚁一开始朝向西侧,为 0 则表示朝向东侧。

输出格式

输出到标准输出。

输出仅一行,包含 N 个数,第 i 个数表示第 i 只蚂蚁爬下绳子所花时间,四舍五入保留到整数。若第 i 只蚂蚁无法爬下绳子,则输出的第 i 个数为 -1。

样例输入
3 6
1 3 5
1 1 0
样例输出
5 5 3
样例解释
第三只蚂蚁在爬行 1 个单位时间后遇见第二只蚂蚁并掉头,再爬行 2 个单位时间到西侧树木;

第二只蚂蚁在爬行 1 个单位时间后遇见第三只蚂蚁并掉头,再爬行 1 个单位时间后遇见第一只蚂蚁并掉头,再爬行 3 个单位时间到西侧树木;

第一只蚂蚁在爬行 2 个单位时间后遇见第二只蚂蚁并掉头,再爬行 3 个单位时间到东侧树木。

子任务
子任务1(17分)
1≤N≤10,L≤1e5。

子任务2(19分)
1≤N≤100,L≤1e9。

子任务3(27分)
1≤N≤5000,L≤1e9。

子任务4(37分)
1≤N≤1e5,L≤1e9。

题目思路

其实写过这个类似的,但是不知道分别求每一个的。

注意到两个性质:
以某只蚂蚁为分界,其东侧蚂蚁全部从东侧爬下绳子,西侧蚂蚁全部从西侧爬下绳子。在不考虑蚂蚁编号的情况下,两只蚂蚁相撞后各自掉头,与相撞后直接穿过是等价的。首先在蚂蚁相撞则穿过的假设下,可以知道从东、西侧下绳子的蚂蚁数国以及它们爬下绳子所花时间。然后便可以在原绳上找到所有从东侧爬下的蚂蚁,并且从东到西给它们分配从小到大排序的耗时,西侧同理。

代码

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<set>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,len,pos[maxn],dir[maxn],w[maxn],e[maxn],numw,nume;
signed main(){
    scanf("%d %d",&n,&len);
    for(int i=1;i<=n;i++){
        scanf("%d",&pos[i]);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&dir[i]);
        if(dir[i]==1){//西
            w[++numw]=len-pos[i];
        }else if(dir[i]==0){//东
            e[++nume]=pos[i];
        }
    }
    sort(w+1,w+1+numw);
    sort(e+1,e+1+nume);
    for(int i=1;i<=nume;i++){//往东
        printf("%d ",e[i]);
    }
    for(int i=numw;i>=1;i--){//往西
        printf("%d ",w[i]);
    }
    return 0;
}

菜鸡的我也就只能补这两题了qwq

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值