2022牛客暑期多校训练营1(总结+补题)

总结:

        今天是暑假的第一把多校,开局我找到了签到题,虽然思路很快就出来了但是因为特判的问题wa了两发才过,罚时++(每次打比赛做签到题的时候都因为太急没有思考好就交导致1-3发的罚时,下次宁愿迟10分钟交也要想好特判改怎么写!),做完签到后继续找签到题花了一些时间,看到A过题数逐渐增加,就去看A,读了好一会题跟队友讨论了十几二十分钟一发过了A,然后就是D,看D时由于我在队友读题读到一半的时候向队友问题意,导致我们在没读完题的情况下就开始做假题,浪费了差不多一个小时的时间,还wa了2发。做出D后我们总共三道题,就分成两拨人分别做I和J题,但最后也没做出来。总的来说,我们签到签的还不够快,遇到签完到之后因为没人会概率期望,所以也没把I题开出来。

题解:

G-Lexicographical Maximum

题意:给一个整数n,问,如果将1-n的所有数字都看成单个字符串,这些字符串中,哪个字符串的字典序最大。

做法:贪心,如果n这个数的前n-1位都是9,则直接输出这个数,否则就输出n-1个数字9。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    string ss;
    cin>>ss;
    int len=ss.length();
    if(count(ss.begin(),ss.begin()+len-1,'9')==len-1)
        cout<<ss<<endl;
    else
    {
        for(int i=1;i<len;i++)
            cout<<9;
    }
    return 0;
}

A-Villages: Landlines

题意:给你1个发电站和n-1个建筑物的坐标和半径,发电站和建筑物都有其坐标及半径,建筑物之间如果存在|x_i-x_j|<=r_i+r_j,则两建筑物连通,无需电线,现要求从发电站通过变电塔向建筑物通电(电必须从发电站经过变电塔到达建筑物),若变电塔如果在发电站和建筑物的范围内,则可以不需要导线连接,反之则需要用导线连接如果变电塔需要连接发电站范围之外的变电塔,则也需要用导线连接。求最少需要多长的导线才能将发电站与所有建筑物相连

做法:我们将所有坐标及半径看成n个坐标轴上的区间,假设发电站的范围内有无数个变电塔,我们就只需要计算从左到右的区间中不连通区域的长度,这个长度就是所要求的导线长度。在输入时,我们将坐标x与半径r转化为区间的左右端点[x-r.x+r],然后对所有区间进行if(L_i!=L_{i+1})Li<L_{i+1},else R_i>R_{i+1}的排序,再扫一遍找到区间中不连通区域的长度即可。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n;
    vector<P> a(n);   
    for(auto &[x,y]:a)
    {
        int l,r;
        cin>>l>>r;
        x=l-r;
        y=l+r;
    }
    function<bool(P,P)> cmp = [&](P aa,P bb)
    {
        if(aa.f==bb.f)
            return aa.s>bb.s;
        return aa.f<bb.f;
    };
    sort(a.begin(),a.end(),cmp);
    int r=a[0].s;
    int ans=0;
    for(int i=1;i<n;i++)
    {
        if(a[i].f>r)
            ans+=a[i].f-r;
        r=max(a[i].s,r);
    }
    cout<<ans<<endl;
    return 0;
}

D-Mocha and Railgun

题意:给定一个点Q和一条以点为中心长度为2d的线段,线段一定在圆内(无论怎么旋转),线段可以围着点Q旋转,问经过旋转后线段投影在原上弧长的最大值。

做法:由观察我们可以知道,线段越靠近的边界,单位长度的线段就能投影出更多的弧长,因此我们可以将线段旋转至与直径平行的位置(即线段的延长线穿过圆心),这样线段在圆上投影出的弧长最长。然后再用余弦公式处理即可。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        int rr;
        int x1,y1,d1;
        double r,x,y,d;
        cin>>rr>>x1>>y1>>d1;
        r=rr;x=x1;y=y1;d=d1;
        double len=sqrt(x*x+y*y);
        double delta=acos((len-d)/r)-acos((len+d)/r);
        cout<<fixed<<setprecision(11)<<delta*r<<endl;
    }   
    return 0;
}

I-Chiitoitsu

题意:有一副麻将,初始给你13张牌(每张牌不超过2张),自己摸牌自己打,问预期要打多少个回合才能凑够七对牌。

做法:概率dp,如果摸到的牌能够凑成一对,就留下,否则就丢掉。令dp[i][j]为当前手牌中有i张单牌,且剩余j张牌没摸时达成七对的数学期望,则有dp方程如下:

\left\{\begin{matrix}1+\frac{j-3}{j} dp[i][j-1](s=1) \\ 1+\frac{3i}{j}dp[i-2][j-1]+\frac{j-3i}{j}dp[i][j-1](s>1) \end{matrix}\right.

单牌数量可以用map计算,假设单牌数量为k,即输出dp[k][123]

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int t;
int n,m,k;
int dp[15][150];
long long POW(long long a,long long b,long long p){
    a%=p;
    long long res=1;
    while(b){
    if(b&1)
        res=res*a%p;
        a=a*a%p;
        b=b>>1;
    }
    return res%p;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    for(int j=3;j<=123;j++)
    {
        int inv=POW(j,mod-2,mod);
        for(int i=1;i<=13;i+=2)
        {
            if(j<3*i)
                continue;
            if(i==1)
                dp[i][j]=(1+(j-3)*inv%mod*dp[i][j-1])%mod;
            else 
                dp[i][j]=(1+((3*i*inv)%mod*dp[i-2][j-1])%mod+(j-3*i)*inv%mod*dp[i][j-1]%mod)%mod;
        }
    }
    cin>>t;
    for(int ca=1;ca<=t;ca++)
    {
        string ss;
        cin>>ss;
        n=ss.length();
        int ans=13;
        map<string,int> mp;
        for(int i=0;i<n;i+=2)
        {
            string s1;
            s1=ss.substr(i,2);
            mp[s1]++;
            if(mp[s1]==2)
                ans-=2;
        }
        cout<<"Case #"<<ca<<": ";
        cout<<dp[ans][123]<<endl;
    }   
    return 0;
}

J-Serval and Essay

题意:给出一张包含n个点的有向图,边\sum_{u_i\in V}^{} \left \{ u_i,v \right \}表示如果要到达v,需要到达指向v的所有u,任取一个起点(该点默认可达),求能够从该选择的点起能够到达的所有点的最大值。

做法:这里用到带记忆的拓扑排序,第一次拓扑排序从1-n中的拓扑不可达点(指拓扑排序中能到达的‘子节点’)开始,可以证明从拓扑不可达点出发所得的结果是不可能比从它的拓扑父节点出发所得的结果大,然后复原in数组。再从1-n中所有剩下的点进行拓扑排序,找到从i开始拓扑排序所能到达的最多的点,即为所求(注意每次拓扑排序后都要复原in数组)。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>t;
    for(int ca=1;ca<=t;ca++)
    {
        cin>>n;
        vector<int> in(n,0),In(n),vis(n,0);
        vector<vector<int>> g(n);
        for(int i=0;i<n;i++)
        {
            cin>>k;
            In[i]=in[i]=k;
            while(k--)
            {
                cin>>m;
                m--;
                g[m].push_back(i);
            }
        }
        for(int i=0;i<n;i++)
        {
            if(!vis[i])
            {
                queue<int> q;
                q.push(i);
                map<int,int> mp;mp[i]++;
                while(!q.empty())
                {
                    int u=q.front();q.pop();
                    for(int x:g[u])
                    {
                        if(x==i)
                            continue;
                        if(!--in[x]&&!vis[x])
                        {
                            q.push(x);
                            vis[x]=1;
                        }
                        mp[x]++;
                    }
                }
                for(auto [x,y]:mp)
                    in[x]=In[x];
            }
        }
        int ans=0;
        for(int i=0;i<n;i++)
        {
            if(!vis[i])
            {
                vis[i]=1;
                queue<int> q;
                q.push(i);
                map<int,int> mp;mp[i]++;
                while(!q.empty())
                {
                    int u=q.front();
                    q.pop();
                    for(int x:g[u])
                    {
                        if(x==i)
                            continue;
                        if(!--in[x])
                        {
                            q.push(x);
                            vis[i]++;
                        }
                        mp[x]++;
                    }
                }
                for(auto [x,y]:mp)
                    in[x]=In[x];
                ans=max(ans,vis[i]);
            }
        }
        cout<<"Case #"<<ca<<": "<<ans<<endl;
    }   
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值