「CF1101F」Trucks and Cities【决策单调优化dp】

F. Trucks and Cities

time limit per test 2 seconds
memory limit per test 256 megabytes
input standard input
output standard output

There are n n ncities along the road, which can be represented as a straight line. The i i i-th city is situated at the distance of a i a_i ai kilometers from the origin. All cities are situated in the same direction from the origin. There are ? trucks travelling from one city to another.

Each truck can be described by 4 integers: starting city s i s_i si, finishing city f i f_i fi, fuel consumption c i c_i ci and number of possible refuelings r i r_i ri. The i i i-th truck will spend c i c_i ci litres of fuel per one kilometer.

When a truck arrives in some city, it can be refueled (but refueling is impossible in the middle of nowhere). The i i i-th truck can be refueled at most r i r_i ri times. Each refueling makes truck’s gas-tank full. All trucks start with full gas-tank.

All trucks will have gas-tanks of the same size V V V litres. You should find minimum possible V V V such that all trucks can reach their destinations without refueling more times than allowed.

Input

First line contains two integers n n n and m m m ( 2 ≤ n ≤ 400 , 1 ≤ m ≤ 250000 ) (2≤n≤400, 1≤m≤250000) (2n400,1m250000) — the number of cities and trucks.

The second line contains n n n integers a 1 , a 2 , … , a n ( 1 ≤ a i ≤ 1 0 9 , a i < a i + 1 ) a_1,a_2,…,a_n (1≤a_i≤10^9, a_i<a_i+1) a1,a2,,an(1ai109,ai<ai+1) — positions of cities in the ascending order.

Next m m m lines contains 4 4 4 integers each. The i i i-th line contains integers s i , f i , c i , r i ( 1 ≤ s i < f i ≤ n , 1 ≤ c i ≤ 1 0 9 , 0 ≤ r i ≤ n ) s_i, f_i, c_i, r_i (1≤s_i<f_i≤n, 1≤c_i≤10^9, 0≤r_i≤n) si,fi,ci,ri(1si<fin,1ci109,0rin) — the description of the i i i-th truck.

Output

Print the only integer — minimum possible size of gas-tanks V V V such that all trucks can reach their destinations.

Example

input
7 6
2 5 7 10 14 15 17
1 3 10 0
1 7 12 7
4 5 13 3
4 7 10 1
4 7 10 1
1 5 11 2
output
55

Note

Let’s look at queries in details:

the 1-st truck must arrive at position 7 from 2 without refuelling, so it needs gas-tank of volume at least 50.
the 2-nd truck must arrive at position 17 from 2 and can be refueled at any city (if it is on the path between starting point and ending point), so it needs gas-tank of volume at least 48.
the 3-rd truck must arrive at position 14 from 10, there is no city between, so it needs gas-tank of volume at least 52.
the 4-th truck must arrive at position 17 from 10 and can be refueled only one time: it’s optimal to refuel at 5-th city (position 14) so it needs gas-tank of volume at least 40.
the 5-th truck has the same description, so it also needs gas-tank of volume at least 40.
the 6-th truck must arrive at position 14 from 2 and can be refueled two times: first time in city 2 or 3 and second time in city 4 so it needs gas-tank of volume at least 55.

  • 题意

    • 就是 x x x轴上给定 n n n个点「 n n n范围 400 400 400,点位置范围 1 0 9 10^9 109 2 × 1 0 5 2\times 10^5 2×105次询问,表示询问从点a到b之间的所有线段整合成k个连续段的最大连续段长度最小
  • 题解

    • 很容易想到的是直接二分找出这个最小连续段,复杂度 O ( n × m × log ⁡ 1 0 9 ) O(n \times m \times \log 10^9) O(n×m×log109)我也这样写了一发,虽然知道稳 T T T,不过还能过43组数据
    •   #include
      
        using namespace std;
        const int maxn=250010;
        typedef long long ll;
        
        int ans[maxn],n,m,a[405],b[maxn][4];
        
        bool check(int cur,int k)
        {
        	int last=b[cur][0],cnt=0;
        	int s=b[cur][0];
        	while(s<b[cur][1]){
        		while(s+1<=b[cur][1]&&a[s+1]-a[last]<=k){
        			s++;
        		}
        		if(last==s) return false;
        		last=s;
        		cnt++;
        	}
        	return cnt-1<=b[cur][3];
        }
        
        int main()
        {
        	scanf("%d %d",&n,&m);
        	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        	for(int i=1;i<=m;i++) {
        		scanf("%d %d %d %d",&b[i][0],&b[i][1],&b[i][2],&b[i][3]);
        		int l=1,r=a[b[i][1]]-a[b[i][0]]+1;
        		while(l<r){
        			int mid=(l+r)>>1;
        			if(check(i,mid)) r=mid;
        			else l=mid+1;
        		}
        		ans[i]=r;
        	}
        	ll res=0;
        	for(int i=1;i<=m;i++) res=max(res,(ll)b[i][2]*ans[i]);
        	printf("%lld\n",res);
        
        }
      
    • 然后考虑用 d p dp dp解决吧,先来看看一个小一点的问题,怎么把一段区间分成若干个连续区间,最大区间长度最小。不妨定义 d p [ i ] [ j ] dp[i][j] dp[i][j]表示把前i个点对应的所有线段组合成j段连续线段的最大长度最小,然后转移的时候,对于 i i i起那面的某个点,如果最优决策中最后一段连续区间的起点在 j j j处,那么前 j j j个点对应的线段一定是要划分成最优的,也就是前j段组合成 k − 1 k-1 k1段的最大连续线段长度最小,所以就可以写转移方程式了: d p [ i ] [ k ] = m i n ( d p [ i ] [ k ] , m a x ( d p [ j ] [ k − 1 ] , a [ i ] − a [ j ] ) ) dp[i][k]=min(dp[i][k],max(dp[j][k-1],a[i]-a[j])) dp[i][k]=min(dp[i][k],max(dp[j][k1],a[i]a[j]))
      显然这个复杂度是 O ( n 3 ) O(n^3) O(n3),而且这个求的是线段前缀的答案,如果是所有区间的答案,则是 O ( n 4 ) O(n^4) O(n4) 对于 n = 400 n=400 n=400的数据是跑不过去的,出题人就是为了卡你这个做法范围放到了400
    • 接着考虑怎么优化这个 d p dp dp吧,对于同一个k(分成的段数),假设需要求的两个区间 [ l , r 1 ] [l,r_1] [l,r1] [ l , r 2 ] ( r 1 < r 2 ) [l,r_2](r_1<r_2) [l,r2](r1<r2)那么第二个区间的最优决策对应的最后一个区间左端点 j 2 j_2 j2一定大于等于 j 1 j_1 j1,显然根据感性思想这是对的,具体严格的证明还在想。。。先假设这是对的吧,然后就好办了,对于同一个 k k k,每次不用都从 1 1 1开始找,因为决策具有单调性,用另外一个指针 p o i n t point point就行了,降维到 O ( n 3 ) O(n^3) O(n3)
  • 代码

      #include
    
      using namespace std;
      typedef long long ll;
      
      int n,m,b,c,d,e,dp[405][405][405],a[405];
      
      int main()
      {
      	scanf("%d %d",&n,&m);
      	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
      	for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) dp[i][j][0]=a[j]-a[i];
      	for(int k=1;k<=n;k++){
      		for(int i=1;i<=n;i++){
      			int point = i;
      			for(int j=i+1;j<=n;j++){
      				while(point<j&&max(dp[i][point][k-1],a[j]-a[point])>max(dp[i][point+1][k-1],a[j]-a[point+1])) ++point;
      				dp[i][j][k]=max(dp[i][point][k-1],a[j]-a[point]);
      			}
      		}
      	}
      	ll ans=0;
      	for(int i=1;i<=m;i++) {
      		scanf("%d %d %d %d",&b,&c,&d,&e);
      		ans=max(ans,1ll*dp[b][c][e]*d);
      	}
      	printf("%lld\n",ans);
      }
    

你可能感兴趣的:(dp,Codeforces)