abc326题解
E - Revenge of “The Salary of AtCoder Inc.”
题意:给定一个长为N的数列a,掷有N个面的骰子,若第i次掷到的数 x i x_{i} xi大于上一个掷到的数 x i − 1 x_{i-1} xi−1,则获得 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=0i−1pk×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 N≤80
解答:可以发现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} Ak≥Li,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×(j−1),代表如果 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;
}