传送门:https://www.luogu.org/problemnew/show/P3324
首先瞅一眼数据范围,发现m, n都很小,所以就可以初步断定这是一道网络流的题。
因为题中说每一个武器只能***特定的机器人,所以可以想象成这把武器有一条指向该机器人的边,那流量是多少呢?这是不确定的,因为武器***机器人的策略是不知道的。不过有一点可以确定,就是在时间t内,这把武器造成的伤害一定是b[i] * t,如果把武器i和源点连一条边,那么这条边的容量就是b[i] * t。
在考虑每一个机器人的装甲值,如果给个机器人i和汇点建立一条边,那么容量就是a[i]。这样我们就成功建立了一个图。
然后对 t 进行二分,如果在当时间为 t 时,到汇点的流量是sum(a[i]),那么就说明所有机器人都被打败了,就在左区间二分,否则说明 t 取小了,就在右区间二分。
因为题中说输出结果与标准答案的绝对误差不超过10-3即视为正确,所以我们把防御值扩大10000倍,这样在long long 范围内就可以解决了。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<stack> 9 #include<queue> 10 #include<vector> 11 using namespace std; 12 #define enter printf("\n") 13 #define space printf(" ") 14 #define Mem(a) memset(a, 0, sizeof(a)); 15 typedef long long ll; 16 typedef double db; 17 const int INF = 0x3f3f3f3f; 18 const db eps = 1e-8; 19 const int maxn = 2e5 + 5; 20 inline ll read() 21 { 22 ll ans = 0; 23 char ch = getchar(), last = ' '; 24 while(!isdigit(ch)) {last = ch; ch = getchar();} 25 while(isdigit(ch)) 26 { 27 ans = ans * 10 + ch - '0'; ch = getchar(); 28 } 29 if(last == '-') ans = -ans; 30 return ans; 31 } 32 inline void write(ll x) 33 { 34 if(x < 0) putchar('-'), x = -x; 35 if(x >= 10) write(x / 10); 36 putchar(x % 10 + '0'); 37 } 38 //mrclr// 39 40 int n, m, t; 41 ll a[60], b[60]; 42 bool s[60][60]; 43 struct Edge 44 { 45 int from, to; 46 ll cap, flow; 47 }; 48 vector<Edge> edges; 49 vector<int> G[maxn]; 50 51 void init() 52 { 53 edges.clear(); 54 for(int i = 0; i < maxn; ++i) G[i].clear(); 55 56 } 57 void addEdge(int from, int to, ll w) //以下就是网络流的板子了 58 { 59 edges.push_back((Edge){from, to, w, 0}); 60 edges.push_back((Edge){to, from, 0, 0}); 61 int sz = edges.size(); 62 G[from].push_back(sz - 2); 63 G[to].push_back(sz - 1); 64 } 65 66 int dis[maxn]; 67 bool vis[maxn]; 68 bool bfs() 69 { 70 Mem(vis); 71 queue<int> q; 72 q.push(0); vis[0] = 1; 73 dis[0] = 0; 74 while(!q.empty()) 75 { 76 int now = q.front(); q.pop(); 77 for(int i = 0; i < (int)G[now].size(); ++i) 78 { 79 Edge& e = edges[G[now][i]]; 80 if(!vis[e.to] && e.cap > e.flow) 81 { 82 vis[e.to] = 1; 83 dis[e.to] = dis[now] + 1; 84 q.push(e.to); 85 } 86 } 87 } 88 return vis[t]; 89 } 90 int cur[maxn]; 91 ll dfs(int now, ll a) 92 { 93 if(now == t || !a) return a; 94 ll flow = 0, f; 95 for(int& i = cur[now]; i < (int)G[now].size(); ++i) 96 { 97 Edge& e = edges[G[now][i]]; 98 if(dis[e.to] == dis[now] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) 99 { 100 e.flow += f; 101 edges[G[now][i] ^ 1].flow -= f; 102 flow += f; a -= f; 103 if(!a) break; 104 } 105 } 106 return flow; 107 } 108 109 ll maxflow() 110 { 111 ll flow = 0; 112 while(bfs()) 113 { 114 Mem(cur); 115 flow += dfs(0, (ll)INF * INF); 116 } 117 return flow; 118 } 119 120 ll sum = 0; 121 122 bool judge(ll tp) 123 { 124 init(); 125 for(int i = 1; i <= m; ++i) addEdge(0, i, b[i] * tp); //把武器和源点建立一条容量为b[i] * time 的边 126 for(int i = 1; i <= n; ++i) addEdge(i + m, t, a[i]); //把机器人和汇点建立一条容量为a[i]的边 127 for(int i = 1; i <= m; ++i) 128 for(int j = 1; j <= n; ++j) 129 if(s[i][j]) addEdge(i, j + m, INF); 130 //如果武器i能***机器人j,就建边,容量无穷,因为武器i有b[i] * time的限制 131 // printf("%lld %lld\n", maxflow(), sum); 132 //调试的时候请不要这么写,因为maxflow()跑完后已经成残量网络了,在上面跑自然不对 133 return maxflow() == sum; 134 } 135 136 int main() 137 { 138 n = read(); m = read(); //防御值扩大1e4倍 139 t = n + m + 1; 140 for(int i = 1; i <= n; ++i) {a[i] = read(); a[i] *= 10000; sum += a[i];} 141 for(int i = 1; i <= m; ++i) b[i] = read(); 142 for(int i = 1; i <= m; ++i) 143 for(int j = 1; j <= n; ++j) 144 {int x = read(); s[i][j] = x;} 145 ll L = 0, R = (ll)INF * (ll)INF; 146 while(L < R) 147 { 148 int mid = (L + R) >> 1; 149 if(judge(mid)) R = mid; 150 else L = mid + 1; 151 } 152 printf("%.5lf\n", (double)L / 10000); 153 return 0; 154 }