// Arup Guha
// 5/11/2017
// Solution to 2015 NAIPC Problem F: Sand Art
// Used my old solution idea but plugged in Josh Linge's flow code since mine was too slow.

import java.util.*;
import java.io.*;

public class sandart {

	final public static double EPSILON = 1e-4;

	// Lots of input for this problem.
	public static int numSections;
	public static int numColors;
	public static int width;
	public static int height;
	public static double[] sand;
	public static double[] sectionSand;
	public static double[] sectionWidth;
	public static double[][] min;
	public static double[][] max;

	// Stores how much we'll need for each flow case - bad hack to avoid code duplication.
	public static double totalNeed;

	public static void main(String[] args) throws Exception {

		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));

		// Read first line of input.
		StringTokenizer tok = new StringTokenizer(stdin.readLine());
		numSections = Integer.parseInt(tok.nextToken());
		numColors = Integer.parseInt(tok.nextToken());
		width = Integer.parseInt(tok.nextToken());
		height = Integer.parseInt(tok.nextToken());

		// Second line.
		tok = new StringTokenizer(stdin.readLine());
		sand = new double[numColors];
		for (int i=0; i<numColors; i++)
			sand[i] = Double.parseDouble(tok.nextToken());

		// Third line - subtract as you go to get widths of each area.
		tok = new StringTokenizer(stdin.readLine());
		sectionWidth = new double[numSections];
		double cur = 0;
		for (int i=0; i<numSections-1; i++) {
			double next = Double.parseDouble(tok.nextToken());
			sectionWidth[i] = next-cur;
			cur = next;
		}
		sectionWidth[numSections-1] = width - cur;

		// Read in the min grid.
		min = new double[numSections][numColors];
		for (int i=0; i<numSections; i++) {
			tok = new StringTokenizer(stdin.readLine());
			for (int j=0; j<numColors; j++)
				min[i][j] = Double.parseDouble(tok.nextToken());
		}

		// Read in the max grid.
		max = new double[numSections][numColors];
		for (int i=0; i<numSections; i++) {
			tok = new StringTokenizer(stdin.readLine());
			for (int j=0; j<numColors; j++)
				max[i][j] = Double.parseDouble(tok.nextToken());
		}

		// Just subtract out the sand for each of these sections from the get go.
		sectionSand = new double[numSections];
		for (int i=0; i<numSections; i++) {
			for (int j=0; j<numColors; j++) {
				sand[j] -= min[i][j];
				sectionSand[i] += min[i][j];
			}
		}

		// Our max's change accordingly.
		for (int i=0; i<numSections; i++)
			for (int j=0; j<numColors; j++)
				max[i][j] -= min[i][j];

		// ok for doubles
		double[] curHeights = new double[numSections];
		for (int i=0; i<numSections; i++)
			curHeights[i] = sectionSand[i]/sectionWidth[i];

		// Find the minimum and maximum heights.
		double minHeight = curHeights[0], maxHeight = curHeights[0];
		for (int i=1; i<numSections; i++) {
			minHeight = Math.min(minHeight, curHeights[i]);
			maxHeight = Math.max(maxHeight, curHeights[i]);
		}

		// Set parameters for binary search.
		double low = minHeight, high = maxHeight;
		double top = high;

		// Run binary search on height.
		while (high-low > EPSILON) {

			// Try filling sand to level mid.
			double mid = (low+high)/2;

			Dinic g = new Dinic(numColors+numSections);
			makeFlowGraph(g, mid);

			double res = g.flow();

			// Adjust limits based on search.
			if (Math.abs(res-totalNeed) < EPSILON)
				low = mid;
			else
				high = mid;
		}

		// For printing and rounding...
		double res = top-low;
		if (Math.abs(res) > EPSILON) res -= EPSILON;
		System.out.printf("%.3f\n", res);
	}

	public static void makeFlowGraph(Dinic g, double mid) {

		// Will put an edge from the source to each color.
		for (int i=0; i<numColors; i++)
			g.add(g.s, i, sand[i], 0);

		int[] needList = new int[numSections];
		Arrays.fill(needList, -1);
		int numNeed = 0;
		for (int i=0; i<numSections; i++)
			if (mid*sectionWidth[i]-sectionSand[i] > 1e-5) /*** SHOULD I CHANGE THIS EPSILON??? ***/
				needList[i] = numNeed++;

		// Edge from each color to each section with how much more of that color can be used for that section.
		for (int i=0; i<numColors; i++)
			for (int j=0; j<numSections; j++)
				if (needList[j] >= 0)
					g.add(i, numColors+j, max[j][i], 0);

		// Connect each section to the source with how much sand is needed to get it to the appropriate height.
		totalNeed = 0;
		for (int i=numColors; i<numColors+numSections; i++) {
			double need = Math.max(0, mid*sectionWidth[i-numColors] - sectionSand[i-numColors]);
			totalNeed += need;
			g.add(i, g.t, need, 0);
		}
	}
}

// An edge connects v1 to v2 with a capacity of cap, flow of flow.
class Edge {
	int v1, v2;
	double cap, flow;
	Edge rev;
	Edge(int V1, int V2, double Cap, double Flow) {
		v1 = V1;
		v2 = V2;
		cap = Cap;
		flow = Flow;
	}
}

class Dinic {

	final public static double EPSILON = 1e-9;

	// Queue for the top level BFS.
	public ArrayDeque<Integer> q;

	// Stores the graph.
	public ArrayList<Edge>[] adj;
	public int n;

	// s = source, t = sink
	public int s;
	public int t;


	// For BFS.
	public boolean[] blocked;
	public int[] dist;

	final public static double oo = 1E9;

	// Constructor.
	public Dinic (int N) {

		// s is the source, t is the sink, add these as last two nodes.
		n = N; s = n++; t = n++;

		// Everything else is empty.
		blocked = new boolean[n];
		dist = new int[n];
		q = new ArrayDeque<Integer>();
		adj = new ArrayList[n];
		for(int i = 0; i < n; ++i)
			adj[i] = new ArrayList<Edge>();
	}

	// Just adds an edge and ALSO adds it going backwards.
	public void add(int v1, int v2, double cap, double flow) {
		Edge e = new Edge(v1, v2, cap, flow);
		Edge rev = new Edge(v2, v1, 0, 0);
		adj[v1].add(rev.rev = e);
		adj[v2].add(e.rev = rev);
	}

	// Runs other level BFS.
	public boolean bfs() {

		// Set up BFS
		q.clear();
		Arrays.fill(dist, -1);
		dist[t] = 0;
		q.add(t);

		// Go backwards from sink looking for source.
		// We just care to mark distances left to the sink.
		while(!q.isEmpty()) {
			int node = q.poll();
			if(node == s)
				return true;
			for(Edge e : adj[node]) {
				if(e.rev.cap > e.rev.flow + EPSILON && dist[e.v2] == -1) {
					dist[e.v2] = dist[node] + 1;
					q.add(e.v2);
				}
			}
		}

		// Augmenting paths exist iff we made it back to the source.
		return dist[s] != -1;
	}

	// Runs inner DFS in Dinic's, from node pos with a flow of min.
	public double dfs(int pos, double min) {

		// Made it to the sink, we're good, return this as our max flow for the augmenting path.
		if(pos == t)
			return min;
		double flow = 0;

		// Try each edge from here.
		for(Edge e : adj[pos]) {
			double cur = 0;

			// If our destination isn't blocked and it's 1 closer to the sink and there's flow, we
			// can go this way.
			if(!blocked[e.v2] && dist[e.v2] == dist[pos]-1 && e.cap - e.flow > EPSILON) {

				// Recursively run dfs from here - limiting flow based on current and what's left on this edge.
				cur = dfs(e.v2, Math.min(min-flow, e.cap - e.flow));

				// Add the flow through this edge and subtract it from the reverse flow.
				e.flow += cur;
				e.rev.flow = -e.flow;

				// Add to the total flow.
				flow += cur;
			}

			// No more can go through, we're good.
			if(Math.abs(flow-min) < EPSILON)
				return flow;
		}

		// mark if this node is now blocked.
		blocked[pos] = Math.abs(flow-min) < EPSILON;

		// This is the flow
		return flow;
	}

	public double flow() {
		clear();
		double ret = 0;

		// Run a top level BFS.
		while(bfs()) {

			// Reset this.
			Arrays.fill(blocked, false);

			// Run multiple DFS's until there is no flow left to push through.
			ret += dfs(s, oo);
		}
		return ret;
	}

	// Just resets flow through all edges to be 0.
	public void clear() {
		for(ArrayList<Edge> edges : adj)
			for(Edge e : edges)
				e.flow = 0;
	}
}