牛客多校 1 (字典序 模拟 几何 斜率 启发式合并+缩边 期望dp )

 ACDGIJ 

G-Lexicographical Maximum

题意:

给一个数字n,找到0~n中字典序最大的数字

注意:

99和996相比,996的字典序最大;个位数的话就是数字本身

代码:

我的和同学的

#include<bits/stdc++.h>
using namespace std;
int main()
{
    bool f=0,to=0;string s;cin>>s;int n=s.size();
    for(int i=0;i<n;i++){
        if(i==n-1)to=1;
        if(s[i]!='9'){f=1;break;}
    }
    if(n==1)cout<<s;
    else if(!f)cout<<s;
    else {
        for(int i=0;i<n-1;i++)cout<<'9';
        if(to)cout<<s[n-1];
    }
    return 0;
}
#include <bits/stdc++.h>
using namespace std;
string s;
int main ()
{
    cin>>s;int length=s.length();
    if (length<2)cout<<s;
    else{
        int cnt=0;
        for (int i=1;i<length;++i){cout<<9;if (s[i-1]=='9')++cnt;}
        if (cnt==length-1)cout<<s[length-1];
    }
}

A-Villages: Landlines

题意:

给电力塔的坐标和有效范围,就需要连接所有塔的电线最小长度

(感觉这道题就是题目理解上困难)

代码:

#include<bits/stdc++.h>
using namespace std;
struct node{
    int l,r;
}e[200005];
bool cmp(node &x,node &y){return x.l<y.l;}
int main()
{
    int n;cin>>n;int x,r;
    for(int i=0;i<n;i++){cin>>x>>r;e[i].l=x-r;e[i].r=x+r;}
    sort(e,e+n,cmp);
    int ans=0,maxx=e[0].r;
    for(int i=1;i<n;i++){
        if(e[i].l>maxx)ans+=(e[i].l-maxx);
        maxx=max(maxx,e[i].r);
    }
    cout<<ans<<'\n';
    return 0;
}

D-Mocha and Railgun

题意:普通的几何题目

代码:

#include <bits/stdc++.h>
using namespace std;
int T;long long r;long long xq, yq, d;
int main ()
{
    cin >> T;
    for (int i=1;i<=T;++i){
        cin>>r;cin>>xq>>yq>>d;
        double dis=sqrt(1.0*xq*xq+1.0*yq*yq);
        double shu=sqrt( 1.0*r*r-1.0*(dis-d)*(dis-d) );
        double shuyou=sqrt( 1.0*r*r-1.0*(dis+d)*(dis+d) );
        double fin=sqrt(1.0*(shu-shuyou)*(shu-shuyou)+4.0*d*d);
        double sina=0.5*fin/r;
        double a=asin(sina);
        cout<<fixed<<setprecision(12)<<2.0*a*r<<endl;
    }
}

C-Grab the Seat!

题目:

有nm列的座位,范围是1≤ x ≤n, 1≤ y ≤m范围里的整数。(0, 1) 到 (0, m) 的线段是屏幕。已有k个人坐在位置上,q次修改,每次修改询问屏幕不会被人遮挡的位置的个数2≤n,m≤2×1e5,1≤k≤2×1e5,1≤q≤200

 

注意:

k是1~k;eps不能去太大

代码:

#include<bits/stdc++.h>
using namespace std;
#define eps 1e-9
int p[200005]={0},x[200005],y[200005],m1[200005],m2[200005];
int n,m,k,q;
int main()
{
    cin>>n>>m>>k>>q;
    for(int i=1;i<=k;i++)cin>>x[i]>>y[i];
    while(q--){
        long long ans=0;
        int pi,xi,yi;
        cin>>pi>>xi>>yi;
        x[pi]=xi,y[pi]=yi; 
        for(int i=1;i<=m;i++)p[i]=n+1;
        for(int i=1;i<=k;i++)p[y[i]]=min(p[y[i]],x[i]);
        double kk=0;
        for(int i=1;i<=m;i++){
            kk=max(kk,1.0*(i-1)/p[i]);
            if(kk==0)m1[i]=p[i]-1;
            else m1[i]=1.0*(i-1)/kk-eps;
        }
        kk=0;
        for(int i=m;i>0;i--){
            kk=min(kk,1.0*(i-m)/p[i]);
            if(kk==0) m2[i]=p[i]-1;
            else m2[i]=1.0*(i-m)/kk-eps;
            ans+=min(m1[i],m2[i]); 
        }
        cout<<ans<<'\n';
    }
    return 0;
}

J-Serval and Essay

题目:

有一张n个点m条边的无重边无自环的有向图;初始时选择一个点染黑,其余点均为白点;若某个点所有入边的起点均为黑点,则该点可以被染黑;最大化图中黑点数量。

思路:

如果一个点B的入度(A)为1,那么A的出边连接到B的出边连接的点,连接的点的入点为A

用启发式合并,出边的size小的作为儿子,因为要合并边,每个儿子的出边连接的点的from都要变。

注意:

1.如何遍历set

2.如何根据要求建图

3.在merge中如果*it的入度为1,那么记得迭代,因为可能x和y都连着*it,同时y是x的父亲,这样的话如果不迭代就会漏掉

代码:

#include<bits/stdc++.h>
using namespace std;
int fa[200005],sz[200005];
set<int>to[200005],fr[200005];
int find(int x){
    return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
void merge(int x,int y){
    vector<pair<int,int>>vc;
    x=find(x),y=find(y);
    if(x==y)return;
    if(to[x].size()>to[y].size())swap(x,y);
    sz[y]+=sz[x];
    fa[x]=y;
    for(set<int>::iterator it=to[x].begin();it!=to[x].end();++it){
        to[y].insert(*it);
        fr[*it].erase(x);
        fr[*it].insert(y);
        if(fr[*it].size()==1)vc.push_back({*it,y});
    }
    for(int i=0;i<vc.size();i++)merge(vc[i].first,vc[i].second);
}
int  main()
{
    int T;cin>>T;int cou=1;
    while(T--){
        int n;cin>>n;
        for(int i=1;i<=n;i++){sz[i]=1;fa[i]=i;to[i].clear();fr[i].clear();}
        for(int i=1;i<=n;i++){
            int x,bi;cin>>x;
            for(int j=1;j<=x;j++){
                cin>>bi;to[bi].insert(i);fr[i].insert(bi);
            }
        }
        for(int i=1;i<=n;i++){
            if(fr[i].size()==1){merge(i,*fr[i].begin());}
        }
         int ans=0;
        for(int i=1;i<=n;++i){ans=max(ans,sz[i]);}
        cout<<"Case #"<<cou++<<": "<<ans<<'\n';
    }
    
    return 0;
}

I-Chiitoitsu

题意:

手中有13张牌,要做7对子,起手的牌同一个花色最多2张牌,求做成的期望轮次。

思路:

设状态转移方程dp[i][j]表示摸牌次数,i是手中单牌的数量,j是牌山中剩余牌的数量。

1.摸到的牌可以和手上的牌凑对,留下 其概率为:(i * 3) / j (因为手中有i张单牌,那么牌堆中每种单牌都有三张) ②

2.摸到的牌不能和手上的牌凑对,丢掉 其概率为:(j - 3 * i) / j

故 dp[i] [j] = 1 + (3 * i) / j * dp[i - 2][j - 1] + (j - 3 * i) / j * dp[i] [j - 1]

期望=可能性*数量

指增加了一次摸牌次数,如果留下,则加上(3 * i) / j * dp[i - 2][j - 1],若丢弃,则(j - 3 * i) / j * dp[i] [j - 1]

初始化:

只有一张牌没有凑齐了,要么我拿到了,次数加一,要么没拿到,期望加上 (i-3)/3*dp[1][i-1] 

故 dp[1][i]=1+(i-3)/3*dp[1][i-1] ;

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int mod=1e9+7;
int dp[20][200];
int T;
ll ksm(ll a,ll k,ll mod)
{
    ll res = 1;
    while(k){
        if(k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}
signed main ()
{
    for(int i=3;i<=123;i++){
        dp[1][i]=(1+(i-3)*ksm(i,mod-2,mod)%mod*dp[1][i-1])%mod;
    }
    for(int i=3;i<=13;i+=2){
        for(int j=3;j<=123;j++){
            dp[i][j]=(1+3*i*ksm(j,mod-2,mod)%mod*dp[i-2][j-1]%mod
                +(j-3*i)*ksm(j,mod-2,mod)%mod*dp[i][j-1]%mod)%mod;
        }
    }
    cin >> T;
    for (int _= 1; _<= T; _++){
      unordered_map<string,int> mp;
        string s;cin >> s;
        int n = 0;
        for(int i = 0;i < s.length();i += 2){
            string tmp = "";
            tmp += s[i];
            tmp += s[i + 1];
            mp[tmp] ++;
        }
        for(int i = 0;i < s.length();i += 2){
            string tmp = "";
            tmp += s[i];
            tmp += s[i + 1];
            if(mp[tmp] == 1) n ++;
        }
        cout << "Case #" <<_<< ": " << dp[n][123] << '\n';
    }
        
}

队友做了B、E,但我还没看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值