AtCoder Beginner Contest 326题解(E,F,G)

abc326题解

E - Revenge of “The Salary of AtCoder Inc.”

题意:给定一个长为N的数列a,掷有N个面的骰子,若第i次掷到的数 x i x_{i} xi大于上一个掷到的数 x i − 1 x_{i-1} xi1,则获得 a x i a_{x_{i}} axi元(初始时x=0),否则停止,问获得钱数的期望。

解答:考虑获得 a i a_i ai的概率 p i p_i pi,枚举骰子投到i之前最后一个投到的数,可为0(i为第一个投到的数)

,1,2,…,i-1。则 p i = ∑ k = 0 i − 1 p k × 1 N p_i=\sum_{k=0}^{i-1} p_k \times \frac{1}{N} pi=k=0i1pk×N1,其中 p 0 = 1 p_0=1 p0=1。然后递推来计算即可。

F - Robot Rotation

题意:给定一个长为N的序列A,初始时从原点朝x轴正方向,每次选择左转或右转然后走 A i A_i Ai长度,问最后能否走到给定的(X,Y)点。 N ≤ 80 N \leq 80 N80

解答:可以发现x轴上移动距离为 ± A 2 ± A 4 ± . . . \pm A_2\pm A_4\pm... ±A2±A4±...,y轴上移动 ± A 1 ± A 3 ± . . . \pm A_1\pm A_3\pm... ±A1±A3±...,每次转向代表正负号可以自由选定。那么问题就转化为规模为40的数列,问能否通过正负号组合为特定的数,但这个规模还是无法直接枚举。考虑二分的思想,将前20个数枚举所有组合,后20个数枚举所有组合,然后遍历前20个数的所有组合,找后20个数组合内有没有与这个组合合起来就是X/Y的。复杂度 O ( N 4 × 2 N 4 ) O(\frac{N}{4}\times 2^{\frac{N}{4}}) O(4N×24N),可以接受。

G - Unlock Achievement

题意:长为N的序列A初始均为1,给定长为N的序列C,代表每次 A i A_i Ai增加1就要花费 C i C_i Ci元,还有M个“成就”,给定M个序列 L i , 1 , L i , 2 , . . . , L i , N L_{i,1},L_{i,2},...,L_{i,N} Li,1,Li,2,...,Li,N,如果 A k ≥ L i , k A_k \geq L_{i,k} AkLi,k恒成立,则可以达成“成就” M i M_i Mi获得 W i W_i Wi元。问最大获利是多少。

解答:可以转化为最小割问题,定义状态 S i , j S_{i,j} Si,j代表 A i A_i Ai达到j,连接 ( S i , j , S i , j + 1 ) (S_{i,j},S_{i,j+1}) (Si,j,Si,j+1),边权为 C i × ( j − 1 ) C_i \times(j-1) Ci×(j1),代表如果 S i , j S_{i,j} Si,j满足而 S i , j + 1 S_{i,j+1} Si,j+1不满足则要缴纳边权这么多的罚金,同样连接 ( S i , j + 1 , S i , j ) (S_{i,j+1},S_{i,j}) (Si,j+1,Si,j)边权为 ∞ \infin ,代表不可能 S i , j + 1 S_{i,j+1} Si,j+1满足而 S i , j S_{i,j} Si,j不满足。连接 ( M k , S i , L k , i ) (M_{k},S_{i,L_{k,i}}) (Mk,Si,Lk,i)边权为 ∞ \infin ,代表不可能 M k M_k Mk满足而 S i , L k , i S_{i,L_{k,i}} Si,Lk,i不满足。源点S代表满足,连接所有 M i M_i Mi,边权为 W i W_i Wi,代表如果 M i M_i Mi不满足要缴纳这么多罚金。汇点T代表不满足,连接所有 S i , 5 S_{i,5} Si,5,边权为 4 × C i 4\times C_i 4×Ci。最后答案就是 ∑ W i \sum W_i Wi减去最小割,根据最大流最小割定理跑一遍最大流即可。

代码:

#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr i64 INF = 0x3f3f3f3f3f3f3f3f;
constexpr int inf=1e9;
struct MaxFlow{
    MaxFlow(int n,int m,int s,int t): n{n},s{s},t{t},tot{0} {
        e.resize(2*m);
        head.assign(n,-1);
        depth.assign(n,-1);
        cur.assign(n,-1);
    }
    struct Edge{
        Edge(): u{-1},v{-1},cap{0},flow{0},next{-1} {}
        int u,v,cap,flow;
        int next;
    };
    vector<Edge> e;
    vector<int> head,depth,cur;
    int n,s,t,tot;
    void addEdge(int u,int v,int w){
        e[2*tot].u=u;
        e[2*tot].v=v;
        e[2*tot].cap=w;
        e[2*tot].next=head[u];
        head[u]=2*tot;
        e[2*tot+1].u=v;
        e[2*tot+1].v=u;
        e[2*tot+1].cap=0;
        e[2*tot+1].next=head[v];
        head[v]=2*tot+1;
        tot++;
    }
    bool bfs(){
        vector<int> vis(n,0);
        depth.assign(n,-1);
        depth[s]=0;
        vis[s]=1;
        queue<int> q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=e[i].next){
                int v=e[i].v;
                if(vis[v]||e[i].cap==0) continue;
                vis[v]=1;
                depth[v]=depth[u]+1;
                q.push(v);
            }
        }
        if(vis[t]) return true;
        return false;
    }
    i64 dfs(int u,i64 flow){
        if(u==t||flow==0){
            return flow;
        }
        i64 ans=0;
        for(int& i=cur[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(depth[v]!=depth[u]+1&&v!=t) continue;
            i64 tmp=dfs(v,min(flow-ans,1ll*e[i].cap));
            e[i].flow+=tmp;
            e[i].cap-=tmp;
            e[i^1].cap+=tmp;
            ans+=tmp;
            if(flow-ans==0){
                break;
            }
        }
        return ans;
    }
    i64 dinic(){
        i64 ans=0;
        while(bfs()){
            cur=head;
            ans+=dfs(s,INF);
        }
        return ans;
    }
};
int main(){
    int n,m;
    cin>>n>>m;
    vector<int> c(n),a(m);
    for(int i=0;i<n;i++){
        cin>>c[i];
    }
    for(int i=0;i<m;i++){
        cin>>a[i];
    }
    vector<vector<int>> l(m,vector<int>(n,0));
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            cin>>l[i][j];
        }
    }
    MaxFlow mf(n*5+m+2,100000,0,1);
    for(int i=0;i<n;i++){
        mf.addEdge(m+2+i*5+4,1,4*c[i]);
        for(int k=0;k<4;k++){
            mf.addEdge(m+2+i*5+k,m+2+i*5+k+1,c[i]*k);
            mf.addEdge(m+2+i*5+k+1,m+2+i*5+k,inf);
        }
    }
    for(int i=0;i<m;i++){
        mf.addEdge(0,i+2,a[i]);
        for(int j=0;j<n;j++){
            mf.addEdge(i+2,m+2+j*5+l[i][j]-1,inf);
        }
    }
    i64 ans=0;
    for(int i=0;i<m;i++){
        ans+=a[i];
    }
    ans-=mf.dinic();
    cout<<ans<<'\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值