7.18 牛客多校

本文介绍了三道算法题目的解题思路和代码实现,涉及区间合并、几何投影和字典序最大数的计算。通过区间合并找到最短连接距离,利用几何原理求解小圆在大圆上的最长弧长,以及如何找到小于给定数的字典序最大数。这些题目锻炼了对区间操作、几何问题和字符串处理的理解与应用。
摘要由CSDN通过智能技术生成

A.Villages: Landlines

题意:题目给出若干个区间,求将各个区间全部相连的最短距离。

分析:先将有重叠部分的区间进行合并,将合并后的区间存在一个数组中,遍历一遍数组就可以了。

反思:①当时真的读题读到心态爆炸,读了两遍都不知道题目在说什么,最后大胆猜测了一下题意,交了一发过了。幸好当时没继续读第四遍....

②区间合并的板子是现拿现用的(我怎么什么都不会 呜呜呜)

代码:

#include<bits/stdc++.h>

using namespace std;
using ll = long long;
int n,x,r;
vector< pair<int,int> > itv; // interval

bool cmp (const pair<int,int>&a, const pair<int,int> &b)  {  return a.first < b.first; }

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x>>r;
        itv.push_back( make_pair(x-r,x+r) );
    }
    
    sort(itv.begin(),itv.end(),cmp);
    int len=itv.size();

    bool flag=false;

    vector< pair<int,int> > res;

    for (int i = 1; i < len; i++) 
    {
        int st = itv[i - 1].first;   
        int ed = itv[i - 1].second;     
        while (i < len && itv[i].first <= ed) 
        { 
            ed = max(ed, itv[i].second);    
            if (i == len - 1) flag = true;  
            i++;                               
        }
        res.push_back({st, ed}); 
    }

    if (!flag) 
    {
        res.push_back({ itv[len-1].first, itv[len-1].second});
    }

    sort(res.begin(),res.end(),cmp);

    int ans=0;
    if(res.size()==1) cout<<"0"<<'\n';
    else
    {
        int rt=res[0].second;
        for(int i=1;i<res.size();i++)
        {
            ans+=res[i].first-rt;
            rt=res[i].second;
        }
        cout<<ans<<'\n';
    }
    return 0; 
}

合并区间的板子:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int n,l,r;

vector< pair<int,int> > itv; // interval
//用pair来存区间,两个元素分别代表区间的起点、终点

bool cmp (const pair<int,int>&a, const pair<int,int> &b)  
{  return a.first < b.first; } //按照起点大小排序

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>l>>r;
        itv.push_back( {l,r} );
    }
    sort(itv.begin(),itv.end(),cmp);

    int len=itv.size(); //区间总数

    bool flag=false;
    vector< pair<int,int> > res; //用来存合并之后的区间
    for (int i = 1; i<len; i++) 
    {
        int st = itv[i - 1].first;   
        int ed = itv[i - 1].second;     
        while (i < len && itv[i].first <= ed) 
        { 
            ed = max(ed, itv[i].second); //右边界逐渐向外延伸   
            if (i == len - 1) flag = true;//标记最后一个区间的合并情况
            i++;                               
        }//循环的目的是顺着一直合并
        res.push_back({st, ed}); //记录合并起来的大区间
    }

    if (!flag) 
    {
        res.push_back({ itv[len-1].first, itv[len-1].second});
    }//处理特殊的尾元素

    sort(res.begin(),res.end(),cmp);
    return 0; 
}

D. Mocha and Railgun

题意:有一个圆心在原点,半径为1的大圆,有一个小圆位于大圆内,给出小圆的圆心和半径,求小圆投影到大圆上的弧长最长为多少。

分析:大胆推测一波,平时或者垂直时取到最值。用画图软件简单画出了几种情况,发现是红线情况下弧长最长。

 去掉无用的情况,简化图形如下所示:

 下图推导求解过程:

 易得∠MON

代码如下:

#include<bits/stdc++.h>
using namespace std;
using LL=long long;
const int N=2e5+5;

int main()
{
    int T; cin>>T;
    while( T-- )
    {        
        int r,x,y,d;
        cin>>r>>x>>y>>d;
        double OQ = sqrt((LL)x * x + (LL)y * y);
        printf("%.12lf\n", (acos((OQ - d) / r) - acos((OQ + d) / r)) * r);         
    }
    return 0;
}

 

G. Lexicographical Maximum

题意:给出一个数n,输出比n小的数中字典序最大的。

分析:要字典序最大,我们要在前面尽可能多地输出9。由于要比n小,我们少输出一位即可。如果整个字符串除了最后一位均为9,我们直接输出原串就行。

代码:

#include<bits/stdc++.h>

using namespace std;
using ll = long long;
string s;
int len;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin>>s;
    ll len=s.size();
    bool flag=true;
    for(int i=0;i<len-1;i++)
    {
        if(s[i]!='9') 
        {
            flag=false;
            break;
        }
    }
    if(!flag) for(int i=1;i<=len-1;i++) cout<<"9";
    else cout<<s;
    cout<<'\n';

    return 0;
}

I. Chiitoitsu

先放代码:

#include<bits/stdc++.h>

using namespace std;
using ll = long long;
const int mod=1000000007;

inline ll qpow(ll a,ll b,ll c){ll res=1%c;a%=c;while(b>0){if(b&1)res=res*a%c;a=a*a%c;b>>=1;}return res;}

int t,T;
vector<vector<ll>> f(30,vector<ll>(150,0));
void getdp()
{
    for(int r=1;r<=125;r++)
    {
        ll inv=qpow(r,mod-2,mod);
        for(int s=1;s<=13;s++)
        {
            if(r<s*3) continue;
            if(s==1) f[s][r] = ( 1 + (r-3)*inv%mod*f[s][r-1] )%mod;
            else     f[s][r] = ( 1 + 3*s*inv%mod*f[s-2][r-1]%mod + (r-3*s)*inv%mod*f[s][r-1]%mod )%mod;
        }
    }
}

void solve()
{
    string s;
    cin>>s;
    map<int,int> Manzu,Pinzu,Souzu,Jihai;
    for(int i=0;i<s.size();i+=2)
    {
        if(s[i+1]=='m') Manzu[s[i]-'0']++;
        if(s[i+1]=='p') Pinzu[s[i]-'0']++;
        if(s[i+1]=='s') Souzu[s[i]-'0']++;
        if(s[i+1]=='z') Jihai[s[i]-'0']++;
    }
    int sgl=0; //single 统计单牌的数量
    for(int i=1;i<=9;i++)
    {
        if(Manzu[i]%2) sgl++;
        if(Pinzu[i]%2) sgl++;
        if(Souzu[i]%2) sgl++;
        if(Jihai[i]%2) sgl++;
    }
    cout<<"Case #"<<T<<": "<<f[sgl][123]<<'\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin>>t;
    getdp();
    for(T=1;T<=t;T++)
    {
        solve();
    }
    return 0;
}

一些需要好好理解的地方:

①逆元的处理

②概率与期望(特别是dp时的方程转移)

③什么样的策略是最优的? 如果摸到的牌刚好能与手上的单牌配对,则留下此牌,另外打出一张单牌。如果摸到的牌不能与手上的单牌配对,则立刻打出摸到的牌。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值