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

总结:

这把牛客是我们目前来说打的最好的一把,开局我和队友一签到,一开始看 G G G 没什么思路,队友一打了个表后我发现了按根号分布的规律,遂跑去写,一发直接过(这题由于其他队伍开局莽,大都wa了1~3发),罚时领先,由于不会第二道签到题 K K K 整个队直接进入了罚坐阶段,我看到 D D D 过了三十多队,还是个图论,遂直接放弃第二道签到,直接莽图论题,而另外两个队友对 K K K 也没想法,他们就去做 H H H ,过了将近三个小时,我 D D D 直接一发过,队友也在 2 m i n 2min 2min 后过了 H H H ,此时我们排名瞬间疯长,队友二用了五分钟时间看出来 J J J 是个最小二乘法(不知道前面为啥没看这题),队友一就迅速开写,第一发wa被卡了精度之后我让他改成 l o n g long long d o u b l e double double

后直接过,四题结束。今天第一次在多校中单独开出铜牌的图论题,爽翻!

题解:

D - Link with Game Glitch

题意:

在一个游戏中可以利用配方将一种物品造出另外一种物品,例如可以使用 a i a_i ai 个物品 b i b_i bi 制造出 c i c_i ci 个物品 d i d_i di ,但很快被发现玩家可以通过一遍又一遍地制作一些特殊的东西来获得无限的资源,于是游戏商就决定添加一个参数 w w w ,将配方中的 c i c_i ci 改成 c i ∗ w c_i*w ciw ,问:最多能将 w w w 设为多少使得玩家不能通过配方生成无限的资源。

做法:我们可以将这个问题转化为图论中环的问题,首先从 b i b_i bi d i d_i di 连一条权值为 c i / a i c_i/a_i ci/ai 的边 ,假设玩家初始有 1 1 1 b i b_i bi 物品,当玩家能通过配方制造出无限的资源时,即存在一个从 b i b_i bi 出发,走过一个环回到 b i b_i bi 时将边权相乘大于 1 1 1 。由于遍边权经过连续相乘后结果可能会爆 d o u b l e double double ,因此我们初始可以将参数 w w w 设置为 1 e − 3 1e-3 1e3 , 这样设置边权可以做到即使每条边权均为最大值 (1e3) ,将带 w 的环的边权相乘后的结果仍然小于 1 1 1 ,然后如果 w w w 符合条件,就将 w w w 倍增,不符合则将上一个 w w w 设为 l l l ,当前的 w w w 设为 r r r ,开始二分,求得结果,其中,可以先使用 S C C SCC SCC 求出每个环中的一个点,然后用这个点跑 s p f a spfa spfa ,判断当前的 w w w 是否能满足条件。

代码:

/*
 author:wuzx
 */

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,double>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
// #define double long double
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
const double eps = 1e-7;
struct scc{
    const int nn;
    int cnt,idx;
    vector<int> col,dfn,low,stk;
    vector<bool> onstk;
    vector<vector<int> > g,comp;
    scc(int n1):nn(n1),cnt(0),idx(0),col(n1),dfn(n1,0),low(n1),stk(n1),onstk(n1,0),g(n1){}
    void add_edge(int u,int v)
    {
        g[u].push_back(v);
    }
    void dfs(int u)
    {
        dfn[u]=low[u]=++cnt;
        stk[idx++]=u;
        onstk[u]=true;
        for(int i=0;i<g[u].size();i++)
        {
            int v=g[u][i];
            if(!dfn[v])
                dfs(v);
            if(onstk[v])
                low[u]=min(low[u],low[v]);
        }
        if(low[u]==dfn[u])
        {
            vector<int> a1;
            while(true)
            {
                int v=stk[--idx];
                onstk[v]=false;
                a1.push_back(v);
                if(u==v) 
                    break;
            }
            comp.push_back(a1);
        }
    }
    void solve()
    {
        for(int i=0;i<nn;i++)
            if(!dfn[i])
                dfs(i);
        int sz=comp.size();
        // reverse(comp.begin(), comp.end()); to sort components in topological
        // order
        for(int i=0;i<sz;i++)
        {
            for(int j=0;j<comp[i].size();j++)
            {
                int x=comp[i][j];
                col[x]=i;
            }
        }
    }
};
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    scc solve(n);
    vector<vector<P > > g(n);
    int a,b,c,d;
    for(int i=0;i<m;i++)
    {
        cin>>a>>b>>c>>d;
        b--;d--;
        solve.add_edge(b,d);
        g[b].emplace_back(d,(double)(c)/(double)(a));
    }
    solve.solve();
    auto col=solve.col;
    vector<P> point;
    for(int i=0;i<n;i++)
        point.emplace_back(col[i],i);
    sort(point.begin(),point.end());
    vector<int> vc(n,-1);
    vector<int> dian;
    for(int i=0;i<n;i++)
    {
        if(i>0&&vc[point[i].f]==-1&&point[i].f==point[i-1].f)
        {
            vc[point[i].f]=1;   
            dian.push_back(point[i].s);
        }
    }
    auto spfa = [&](int st,double w)
    {

        vector<double> dis(n,0);
        vector<int> vis(n,0),cnt(n,0);
        dis[st]=1;
        queue<int> q;
        vis[st]=1;
        q.push(st);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            vis[u]=0;
            for(int i=0;i<g[u].size();i++)
            {
                int v=g[u][i].f;
                double ww=g[u][i].s;
                if(dis[v]<dis[u]*ww*w)
                {
                    dis[v]=dis[u]*ww*w;
                    cnt[v]=cnt[u]+1;
                    if(v==st&&dis[v]>1.0)
                        return false;
                    if(cnt[v]>=n)
                        return false;
                    if(!vis[v])
                        q.push(v),vis[v]=1;
                }
            }
        }
        return true;
    };
    auto check = [&](double w)
    {
        for(int i:dian)
            if(!spfa(i,w))
                return false;
        return true;
    };
    double er=2.0;
    double ori=1e-3;
    double l=1e-3;
    double r=ori*er;
    double fr=1e-3;
    while(1)
    {
        if(check(l))
        {
            fr=l;
            if(r>1.0)
            {
                r=1;
                break;
            }
            l=r;
            er*=er;
            r=ori*er;
        }
        else
        {
            r=l;
            l=fr;
            break;
        }
    }
    double mid=(l+r)/2;
    while(fabs(r-l)>eps)
    {
        mid=(l+r)/2.0;
        bool flag=check(mid);
        if(flag)
            l=mid;
        else
            r=mid;
    }
    cout<<fixed<<setprecision(6)<<r<<endl;
    return 0;
}

G - Link with Monotonic Subsequence

题意:

构造一个 1 ~ n 1~n 1n 的排列使排列的最长不下降子序列与最长不上升子序列的长度最小,即求 m a x ( l i s ( p ) , l d s ( p ) ) max(lis(p),lds(p)) max(lis(p),lds(p))

做法:

由观察我们可知 m a x ( l i s ( p ) , l d s ( p ) ) max(lis(p),lds(p)) max(lis(p),lds(p)) 的值为 K = s q r t ( n − 1 ) + 1 K=sqrt(n-1)+1 K=sqrt(n1)+1 ,我们将1~n中的每 s q r t ( n ) sqrt(n) sqrt(n) 个(后面不够 s q r t ( n ) sqrt(n) sqrt(n)个直接视为一组)元素反转即可。

代码:

/*
 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--)
    {
        cin>>n;
        int x=sqrt(n);
        if(x*x!=n)    
            x++;
        vector<int> a(n);
        for(int i=1;i<=n;i++)
            a[i-1]=i;
        int now=0;
        for(int j=1;j<=n/x;j++)
        {
            reverse(a.begin()+now,a.begin()+now+x);
            now+=x;
        }
        reverse(a.begin()+now,a.end());
        for(int i=0;i<n;i++)
        {
            if(i!=0)
                cout<<" ";
            cout<<a[i];
        }
        cout<<endl;
    }
    return 0;
}

J - Link with Arithmetic Progression

题意:

给你一组数 a i ( 1 ≤ i ≤ n ) a_i(1\le i\le n) ai1in) ,让你求由将 a i ( 1 ≤ i ≤ n ) a_i(1\le i\le n) ai1in) 变为等差数列 a i ‘ ( 1 ≤ i ≤ n ) a^{‘}_i(1\le i\le n) ai1in) 所需的最小代价,

代价计算方式为 ∑ i = 1 n ( a i − a i ‘ ) 2 {\textstyle \sum_{i=1}^{n}}(a_i-a^{‘}_i)^2 i=1n(aiai)2

做法:由这个代价我们不难想到,这个跟高中学的用最小二乘法求直线线性回归拟合方程中回归方程的误差相似。由最小二乘法,直线斜率 k = x y − n x ˉ ⋅ y ˉ x 2 − n ( x ˉ ) 2 k=\frac{xy-n\bar{x}\cdot \bar{y} }{x^2-n(\bar{x})^2} k=x2n(xˉ)2xynxˉyˉ ,再用待定系数法求出 b b b ,得到 y = k x + b y=kx+b y=kx+b

代码:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值