2022天梯赛补题

2 篇文章 0 订阅
1 篇文章 0 订阅

目录

L1

L1-1 今天我要赢                                                                       -分数 5

L1-2 种钻石                                                                              -分数 5

L1-3 谁能进图书馆                                                                   -分数 10

L1-4 拯救外星人                                                                       -分数 10

L1-5 试试手气                                                                           -分数 15

L1-6 斯德哥尔摩火车上的题                                                     -分数 15

L1-7 机工士姆斯塔迪奥                                                            -分数 20

L1-8 静静的推荐                                                                       -分数 20

L2

L2-1 插松枝                                                                              -分数 25

L2-2 老板的作息表                                                                   -分数 25

L2-3 龙龙送外卖                                                                       -分数 25

L2-4 大众情人                                                                           -分数 25

L3

L3-1 千手观音                                                                           -分数 30

L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事   -分数 30

L3-3  教科书般的亵渎                                                               -分数 30


L1

L1-1 今天我要赢                                                                       -分数 5

签到题

代码:

  

L1-2 种钻石                                                                              -分数 5

代码:

L1-3 谁能进图书馆                                                                   -分数 10

比较麻烦,就是按照题目要求写if

代码:

L1-4 拯救外星人                                                                       -分数 10

代码:

L1-5 试试手气                                                                           -分数 15

代码:

L1-6 斯德哥尔摩火车上的题                                                     -分数 15

代码:

L1-7 机工士姆斯塔迪奥                                                            -分数 20

由于每次只会涂一行或者一列所以可以发现安全的位置是n*m-(n*涂了一列的次数-m*涂了一行的次数+涂了一列的次数*涂了一行的次数);

代码:

L1-8 静静的推荐                                                                       -分数 20

可以先把可以通过pat考试通过的添加到答案,然后由于只进行K轮,每次都取一个严格递增数列,那么只需要对每个大于175分数的人数与K取最小值加到答案即可

代码:

  

L2

L2-1 插松枝                                                                              -分数 25

模拟题 ,根据题目要求模拟即可,每次先从小盒子里取松针,如果没有就从推送器上取......值得注意的是有3个要输出松枝的条件。

代码:

//---------------------------↓乱定义区------------------------------------
int n,m,k;
stack<int>he;
queue<int>tui;
deque<int>zhi;
//---------------------------↓乱写乘区------------------------------------
void solve()
{
    cin>>n>>m>>k;
    for(int i=1,j;i<=n;i++)
    {
        cin>>j;
        tui.push(j);
    }
    for(int i=1;i<=n;i++)//一共要插n个,保证一回合插一个即可
    {
        kai:{}
        int f=0;
        if(!he.empty() && (zhi.empty() || zhi.back()>=he.top()))
        {
            f=1;
            zhi.push_back(he.top());he.pop();
        }
        if(!f)
        {
            if(!tui.empty() && (zhi.empty()|| zhi.back()>=tui.front()))
            {
                f=1;
                zhi.push_back(tui.front());tui.pop();
            }else
            {
                while(!tui.empty() && he.size()<m && zhi.back()<tui.front())
                {
                    he.push(tui.front());tui.pop();
                }
                if(!tui.empty() && (zhi.empty()|| zhi.back()>=tui.front()))
                {
                    f=1;
                    zhi.push_back(tui.front());tui.pop();
                }
            }
        }
        if(!f || zhi.size()==k ||i==n)
        {
            int e=0;
            while(!zhi.empty())
            {
                if(!e)e=1;
                else cout<<" ";
                cout<<zhi.front();
                zhi.pop_front();
            }
            cout<<endl;
        }
        if(!f)goto kai;//这个回合没有插,那么相当于这个回合没有结束,继续插
    }
}
int main()
{
    IO;
	int tn=1;
	//cin>>tn;
	while(tn--)
    {
        solve();
    }
	return 0;
}

L2-2 老板的作息表                                                                   -分数 25

代码:

//---------------------------↓乱定义区------------------------------------
int n,kai=0;
set<pair<int,int>>st;
string s;
//---------------------------↓乱写乘区------------------------------------
void solve()
{
   cin>>n;
   getchar();
   for(int i=0;i<=n;i++)
   {
       getline(cin,s);
       int a,b,c,d,e,f;
       sscanf(s.c_str(),"%d:%d:%d - %d:%d:%d",&a,&b,&c,&d,&e,&f);
       st.insert({a*3600+b*60+c,d*3600+e*60+f});
   }
   for(auto [a,b]:st)
   {
       if(kai<a)printf("%02d:%02d:%02d - %02d:%02d:%02d\n",kai/3600,kai%3600/60,kai%60,a/3600,a%3600/60,a%60);
       kai=b;
   }
   if(kai!=23*3600+59*60+59)
   {
       printf("%02d:%02d:%02d - %02d:%02d:%02d\n",kai/3600,kai%3600/60,kai%60,23,59,59);
   }
}

L2-3 龙龙送外卖                                                                       -分数 25

对于节点a,b,如果a,b互不是对方祖先,可以发现,不论先去a还是b,都得在回去他们的最近公共祖先,然后在去往另一个点,所以每条边对答案的贡献都是2,由于送完最后一趟后不用返回,那么只需要减去从根节点到最远需要到达的点的距离即可。我们可以先从根节点dfs求出到所有点的最短距离,然后根据每个需要到达的点,向上走到没有走过的点统计每条边的贡献即可。

代码:

//---------------------------↓乱定义区------------------------------------
int n,m,root;
int dis[maxn];
int fa[maxn],f[maxn];
vector<int>v[maxn];
//---------------------------↓乱写乘区------------------------------------
void dfs(int rt,int len)
{
    dis[rt]=len;
    for(int i:v[rt])
    {
        dfs(i,len+1);
    }
}
void solve()
{
   cin>>n>>m;
   for(int i=1,j;i<=n;i++)
   {
       cin>>j;
       fa[i]=j;
       if(j==-1)root=i;
       else v[j].pd(i);
   }
   dfs(root,0);
   f[root]=1;
   int ans=0,ma=0;
   for(int i=1,j;i<=m;i++)
   {
       cin>>j;
       ma=max(ma,dis[j]);
       while(!f[j])
       {
           f[j]=1,j=fa[j],ans+=2;
       }
       cout<<ans-ma<<endl;
   }
}

L2-4 大众情人                                                                           -分数 25

 由于朋友的朋友可以通过中间的朋友传递距离,所以可以看出这是个多源路径最短路问题,直接Floyd,然后根据题目要求,找到答案即可。

代码:

//---------------------------↓乱定义区------------------------------------
int n;
string s;
int k;
int f[maxn];
ll dp[maxn][maxn];
//---------------------------↓乱写乘区------------------------------------
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)dp[i][j]=0xfffffffffffff;
    for(int i=1;i<=n;i++)dp[i][i]=0;
    for(int i=1;i<=n;i++)
    {
        cin>>s>>k;
        if(s[0]=='F')f[i]=1;
        for(int j=0,a,b;j<k;j++)
        {
            cin>>s;
            sscanf(s.c_str(),"%d:%d",&a,&b);
            dp[i][a]=b;
        }
    }
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i!=j)dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
            }
        }
    }
    vector<int>ans1,ans2;
    ll m1=0xfffffffffffff,m2=0xfffffffffffff;
    for(int i=1;i<=n;i++)
    {
        ll ma=0;
        for(int j=1;j<=n;j++)
        {
            if(i==j||f[i]==f[j])continue;
            ma=max(ma,dp[j][i]);
        }
        if(f[i])
        {
            if(m1>ma)m1=ma,ans1.clear();
            if(m1==ma)ans1.pd(i);
        }else
        {
            if(m2>ma)m2=ma,ans2.clear();
            if(m2==ma)ans2.pd(i);
        }
    }
    int e=0;
    for(auto i:ans1)
    {
        if(!e)e=1;
        else cout<<" ";
        cout<<i;
    }
    cout<<endl;
    e=0;
    for(auto i:ans2)
    {
        if(!e)e=1;
        else cout<<" ";
        cout<<i;
    }

}

L3

L3-1 千手观音                                                                           -分数 30

拓扑排序题 ,由于给的每个数字都是严格递增的,那么对于每个位数相同的数字,都可以连边,计算入度,例如,1,123,132,222,这3个数字,我们根据123,132,可以推断出2<3,根据123,222可以推断出1<2,那么可以推出1<2<3.,而1和其他数位数不同,不可以直接推出关系。连完边后拓扑排序,因为给出的拓扑排序不一定是可以推出唯一拓扑序的,所以根据题目要求还需要对于同时在queue里的字母,按字典序排序,我们把queue换成priority_queue即可。

代码:

//---------------------------↓乱定义区------------------------------------
int n,cnt;
unordered_map<string,int>id;
unordered_map<int,string>name;
vector<int>v[maxn],ve[maxn];
set<pair<int,int>>st;
int ru[maxn];
//---------------------------↓乱写乘区------------------------------------
void tp()
{
    priority_queue<string,vector<string>,greater<string>>p;
    for(int i=1;i<=cnt;i++)
    {
        if(ru[i]==0)p.push(name[i]);
    }
    int e=0;
    while(!p.empty())
    {
        string now=p.top();p.pop();
        if(!e)e=1;
        else cout<<".";
        cout<<now;
        for(int i:ve[id[now]])
        {
            ru[i]--;
            if(ru[i]==0)p.push(name[i]);
        }
    }
}
void solve()
{
    cin>>n;
    cnt=0;
    for(int i=0;i<n;i++)
    {
        string s,e="";
        cin>>s;
        for(int j=0;j<s.length();j++)
        {
            if(s[j]!='.')
            {
                e+=s[j];
            }
            if(s[j]=='.'|| j==s.length()-1)
            {
                if(!id[e])id[e]=++cnt,name[cnt]=e;
                v[i].pd(id[e]);
                e="";
            }
        }
    }
    for(int i=0;i<n-1;i++)
    {
        if(v[i].size()!=v[i+1].size())continue;
        for(int j=0;j<v[i].size();j++)
        {
            if(v[i][j]==v[i+1][j])continue;
            if(st.find({v[i][j],v[i+1][j]})!=st.end())break;
            st.insert({v[i][j],v[i+1][j]});
            
            ve[v[i][j]].pd(v[i+1][j]);
            ru[v[i+1][j]]++;
            break;
        }
    }
    tp();
}

L3-2 关于深度优先搜索和逆序对的题应该不会很难吧这件事   -分数 30

 根据样例解释可以发现,(1)对于节点5,他的子节点1,2,无论如何排列,在dfs序中都必在5后,而4,对于1,2节点,不是在任何dfs中都有贡献的。

(2)对于节点3、5,由于3是5的双亲节点,那么根据(1)可以知道3必在5的前面,且在所有dfs序排列中都不会有贡献,对于节点5,1,就是在所有dfs序排列中都会产生贡献

(3)在一个1-n不重复序列的全排列中一个数字出现在另一个数字前的概率是1/2,也就是会有n!/ 2个逆序对,而在这个dfs排列中,对于2个非有祖先关系的2个点,其中一个点出现在另一个节点的概率可以发现也是是1/2,又由于2个节点值不同,所以必然会在一半的排列中会产生贡献。

我们不妨对与所有节点对,都假设都会在一半的dfs序排列中都会产生贡献,那么就不需要考虑(3)这个条件了,只需要看对于节点对中有祖先关系的,进行考虑。

对于祖先节点,和一个子节点,如果祖先节点的值比子节点大,那么我们少计算了一半贡献,如果小,那么我们多计算了一半贡献,只需要对应的加减即可。

那么我们只需要dfs遍历,然后通过树状数组(Fenwick Tree / Binary Indexed Tree)计算这个节点的祖先节点有多少比他大,有多少比他小即可。复杂度O(n*log(n))

代码:

//---------------------------↓乱定义区------------------------------------
int n,r;
vector<int>v[maxn];
ll c[maxn],fuda[maxn],fuxiao[maxn],jc[maxn],num[maxn];
ll ans;
//---------------------------↓乱写乘区------------------------------------
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,ll e)
{
    for(;x<=n;x+=lowbit(x))c[x]+=e;
}
ll sum(int x)
{
    ll ans=0;
    for(;x>0;x-=lowbit(x))ans+=c[x];
    return ans;
}
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;
}
void dfs(int rt,int fa)
{
    fuda[rt]=sum(n)-sum(rt);
    fuxiao[rt]=sum(rt-1);
    num[rt]=1;
    add(rt,1);
    for(int &i:v[rt])
    {
        if(i==fa)continue;
        dfs(i,rt);
        num[rt]=num[rt]*num[i]%mod;
    }
    add(rt,-1);
    num[rt]=num[rt]*jc[v[rt].size()-1]%mod;
}
void solve()
{
    cin>>n>>r;
    jc[0]=1;
    for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
    for(int i=1,a,b;i<n;i++)
    {
        cin>>a>>b;
        v[a].pd(b);
        v[b].pd(a);
    }
    v[r].pd(0);
    dfs(r,0);

    ans=0;
    ll two=ksm(2,mod-2);
    ans=1ll*n*(n-1)/2%mod*num[r]%mod*two%mod;

    for(int i=1;i<=n;i++)
    {
        ans+=fuda[i]*num[r]%mod*two%mod;
        ans-=fuxiao[i]*num[r]%mod*two%mod;
        ans=(ans+mod)%mod;
    }
    cout<<ans<<endl;
}

L3-3  教科书般的亵渎                                                               -分数 30

待补状态┭┮﹏┭┮,只想到暴力骗7分,不知道dp咋推

在暴力过程中发现第一张牌不可以理解为K轮每轮造成1点伤害,不然样例推不出来,也就是第一张牌实际上是随机选择一些牌造成总和为K的伤害,每个敌人的血都可以扣成负数。emmm。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值