列表

详情


NC50546. 锯木厂选址

描述

从山顶上到山底下沿着一条直线种植了n棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。
木材只能朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建这两个锯木厂,使得运输的费用总和最小。假定运输每公斤木材每米需要一分钱。
你的任务是编写一个程序,读入树的个数和他们的重量与位置,计算最小运输费用。

输入描述

输入的第一行为一个正整数n,表示树的个数。树从山顶到山脚按照标号。
接下来n行,每行有两个整数w_id_i。分别表示第i棵树的重量(公斤为单位)和第i棵树和第i+1棵树之间的距离。最后一个数d_n,表示第n棵树到山脚的锯木厂的距离。

输出描述

输出仅一个数,表示最小的运输费用。

示例1

输入:

9
1 2
2 1
3 3
1 1
3 2
1 6
2 1
1 2
1 1

输出:

26

说明:

下图展示了对于样例输入的最佳伐木场设置位置,树木用一个圆表示,伐木场用黑色标出。结果为:

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

Java(javac 1.8) 解法, 执行用时: 477ms, 内存消耗: 46928K, 提交时间: 2021-03-06 14:47:42

import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.LockSupport;

public class Main {

    static class Task {

        public static String roundS(double result, int scale) {
            String fmt = String.format("%%.%df", scale);
            return String.format(fmt, result);
            //        DecimalFormat df = new DecimalFormat("0.000000");
            //        double result = Double.parseDouble(df.format(result));
        }

        int rt(int x) {
            if (x != fa[x]) {
                int to = rt(fa[x]);
                //     dp[x] ^= dp[fa[x]];
                fa[x] = to;
                return to;
            }

            return x;
        }

        void combine(int x, int y, int val) {
            int rt1 = rt(x);
            int rt2 = rt(y);

            if (rt1 == rt2)
                return;

            fa[rt1] = rt2;
            //    dp[rt1] = dp[x] ^ dp[y] ^ val;
            g--;

        }


        int fa[];
        int g;


        static int MAXN = 10000;
        static Random rd = new Random(348957438574659L);

        static int[] ch[], val, size, rnd, cnt;
        static int len = 0, rt = 0;

        // return new node, the node below s
        static int rotate(int s, int d) {
            // child
            int x = ch[s][d ^ 1];
            // give me grandson
            ch[s][d ^ 1] = ch[x][d];
            // child become father
            ch[x][d] = s;
            // update size, update new son first
            update(s);
            update(x);
            return x;
        }

        static void update(int s) {
            size[s] = size[ch[s][0]] + size[ch[s][1]] + cnt[s];
        }

        // 0 for left, 1 for right
        static int cmp(int x, int num) {
            if (val[x] == num)
                return -1;

            return num < val[x] ? 0 : 1;
        }

        static int insert(int s, int num) {
            if (s == 0) {
                s = ++len;
                val[s] = num;
                size[s] = 1;
                rnd[s] = rd.nextInt();
                cnt[s] = 1;
            } else {
                int d = cmp(s, num);

                if (d != -1) {
                    ch[s][d] = insert(ch[s][d], num);

                    // father's random should be greater
                    if (rnd[s] < rnd[ch[s][d]]) {
                        s = rotate(s, d ^ 1);
                    } else {
                        update(s);
                    }
                } else {
                    ++cnt[s];
                    ++size[s];
                }
            }

            return s;
        }

        static int del(int s, int num) {
            int d = cmp(s, num);

            if (d != -1) {
                ch[s][d] = del(ch[s][d], num);
                update(s);
            } else if (ch[s][0] * ch[s][1] == 0) {
                if (--cnt[s] == 0) {
                    s = ch[s][0] + ch[s][1];
                }
            } else {
                int k = rnd[ch[s][0]] < rnd[ch[s][1]] ? 0 : 1;
                // k points to smaller random value,then bigger one up
                s = rotate(s, k);
                // now the node with value num become the child
                ch[s][k] = del(ch[s][k], num);
                update(s);
            }

            return s;
        }

        static int getKth(int s, int k) {
            int lz = size[ch[s][0]];

            if (k >= lz + 1 && k <= lz + cnt[s]) {
                return val[s];
            } else if (k <= lz) {
                return getKth(ch[s][0], k);
            } else {
                return getKth(ch[s][1], k - lz - cnt[s]);
            }
        }

        static int getRank(int s, int value) {
            if (s == 0)
                return 1;

            if (value == val[s])
                return size[ch[s][0]] + 1;

            if (value < val[s])
                return getRank(ch[s][0], value);

            return getRank(ch[s][1], value) + size[ch[s][0]] + cnt[s];
        }


        static int getPre(int data) {
            int ans = -1;
            int p = rt;

            while (p > 0) {
                if (data > val[p]) {
                    if (ans == -1 || val[p] > val[ans])
                        ans = p;

                    p = ch[p][1];
                } else
                    p = ch[p][0];
            }

            return ans != -1 ? val[ans] : (-2147483647);
        }

        static int getNext(int data) {
            int ans = -1;
            int p = rt;

            while (p > 0) {
                if (data < val[p]) {
                    if (ans == -1 || val[p] < val[ans])
                        ans = p;

                    p = ch[p][0];
                } else
                    p = ch[p][1];
            }

            return ans != -1 ? val[ans] : 2147483647;
        }


        static boolean find(int s, int num) {
            while (s != 0) {
                int d = cmp(s, num);

                if (d == -1)
                    return true;
                else
                    s = ch[s][d];
            }

            return false;
        }
        static int ans = -10000000;
        static boolean findX(int s, int num) {
            while (s != 0) {
                if (val[s] <= num) {
                    ans = num;
                }

                int d = cmp(s, num);

                if (d == -1)
                    return true;
                else {
                    s = ch[s][d];
                }
            }

            return false;
        }

        long gcd(long a, long b) {
            if (b == 0)
                return a;

            return gcd(b, a % b);
        }


        void linear_sort(int arr[]) {
            int d =  65536;
            ArrayDeque bucket[] = new ArrayDeque[d];

            for (int j = 0; j < d; ++j) {
                bucket[j] = new ArrayDeque();
            }

            for (int u : arr) {
                bucket[u % d].offer(u);
            }

            int pos = 0;

            for (int j = 0; j < d; ++j) {
                while (bucket[j].size() > 0) {
                    arr[pos++] = (int)bucket[j].pollFirst();
                }
            }

            for (int u : arr) {
                bucket[u / d].offer(u);
            }

            pos = 0;

            for (int j = 0; j < d; ++j) {
                while (bucket[j].size() > 0) {
                    arr[pos++] = (int)bucket[j].pollFirst();
                }
            }
        }

        int cur  = 0;
        int h[];
        int to[];
        int ne[];
        void add(int u, int v) {
            to[cur] = v;
            ne[cur] = h[u];
            h[u] = cur++;
        }


        public boolean dfs(String cur, HashMap<String, Integer> vis, Map<String, List<String>> list, int cl) {

            vis.put(cur, cl);

            for (String hp : list.get(cur)) {
                if (vis.containsKey(hp)) {
                    if (vis.get(hp) == cl) {
                        return false;
                    }

                } else {
                    if (!dfs(hp, vis, list, 1 - cl)) {
                        return false;
                    }
                }
            }

            return true;

        }


        public class MultiSet {

            TreeMap<Integer, Integer> map;
            int ct = 0;
            MultiSet() {
                map = new TreeMap<>();
            }

            public void remove(int key) {
                if (map.containsKey(key)) {
                    int times = map.get(key);

                    if (times == 1) {
                        map.remove(key);
                    } else {
                        map.put(key, times - 1);
                    }

                    ct--;
                }
            }

            public void add(int key) {
                map.put(key, map.getOrDefault(key, 0) + 1);
                ct++;
            }

            public int last() {
                return map.lastKey();
            }

            public int first() {
                return map.firstKey();
            }

            public int size() {
                return ct;
            }

        }




        int color[], dfn[], low[], stack[];
        int sccno[];

        boolean iscut[];
        int time = 0, top = 0;
        int scc_cnt;
        int  dcc_cnt;
        List<Integer> dcc[];
        int root = 0;
        //  无向图的强连通分量
        void tarjanNonDirect(int u) {
            low[u] = dfn[u] = ++time;
            stack[top++] = u;
            int child = 0;

            for (int i = h[u]; i != -1; i = ne[i]) {
                int v = to[i];

                if (dfn[v] == 0) {
                    tarjanNonDirect(v);
                    low[u] = Math.min(low[u], low[v]);

                    if (low[v] >= dfn[u]) {
                        if (u != root || ++child > 1) { // 不是root,直接记为cut,是root,判断是否有两个儿子
                            iscut[u] = true;
                        }

                        ++dcc_cnt;
                        // 一个割点可能会被多个点双共享
                        int z = -1;

                        do {
                            z = stack[--top];
                            //dcc[dcc_cnt].add(z);
                        } while (z != v);

                        //dcc[dcc_cnt].add(u);
                    }
                } else {
                    low[u] = Math.min(low[u], dfn[v]);
                    // 没有特判是否直接指向父亲
                    // 回边,使用dfn【v】更新low【u】,因为可能是指向父亲,而父亲的low可能比较小
                }
            }
        }

        long dp11[][];
        public long s(int l, int r, int a[]) {
            if (l > r)
                return 0;

            if (dp11[l][r] != 0) {
                return dp11[l][r];
            }

            if (l == r) {
                return dp11[l][r] = a[l];
            }

            return dp11[l][r] = Math.max((long)a[l] - s(l + 1, r, a), (long)a[r] - s(l, r - 1, a));
        }


        long dp[][];

        long a[];
        long y(int j,int x){
            return dp[j][x-1] + sa[j];
        }
        long x(int j){
            return a[j];
        }
        long k(int j){
            return s[j];
        }



        long xj(long x1,long y1,long x2,long y2,long x3,long y3){
            return (x1-x2)*(y2-y3)-(y1-y2)*(x2-x3);
        }

        boolean bj(long x1,long y1, long x2 ,long y2, long k){
            return (y1-y2) <= (x1-x2)*k;
        }

long s[];
        long f = 1;
        long sa[];
        public void solve(int testNumber, InputReader in, PrintWriter out) {
int fk = 1;int n;

            int q[] = new int[500010];
int k =-1;
        while(fk-->0) {
            try {
                n = in.nextInt();

            }catch (Exception e){
                break;
            }


            s = new long[n + 2];
            long d[] = new long[n + 1];
            a = new long[n+2];
            sa = new long[n+2];

            for (int i = 1; i <= n; ++i) {
                a[i] = in.nextLong();
                d[i] = in.nextLong();
            }

            for(int i=2;i<=n+1;++i){
                s[i] = s[i-1] + d[i-1];
                sa[i] = a[i]*s[i];
                sa[i] += sa[i-1];
                a[i] += a[i-1];
            }


             dp = new long[n + 2][4];



            for(int x=1;x<=3;++x) {
                int st = 0;
                int e = 0;
                q[e++] = 0;


                for (int i = 1; i <= n+1; ++i) {

                    // dp[i][x] = dp[j][x-1] + (s[i] * (a[i] -  a[j]) ) -  (sa[i] - sa[j]);

                    //  y = dp[j][x-1] + sa[j]
                    //  x = a[j];
                    //  k = s[i];


                    // dp[i] =  dp[j] + (s[i]-s[j]) - a[j+1]*(i-j);

                    // y = dp[j] - s[j] + a[j+1]*j;
                    // x = a[j+1];
                    // k = i;

                    // dp[i] =  dp[j] + b*(s[i] - s[j]) + c + a*(s[i]2 - 2*s[i]*s[j] + s[j]2);

                    //  y = dp[j] + a*s[j]*s[j] - b*s[j]
                    //  x = s[j];
                    //  k = 2*a*s[i]

                    while (st + 1 < e && bj(x(q[st + 1]), y(q[st + 1],x), x(q[st]), y(q[st],x), k(i))) st++;

                    dp[i][x] = dp[q[st]][x-1] + (s[i] * (a[i] -  a[q[st]]) ) -  (sa[i] - sa[q[st]]);

             //       if (i + 1 - k >= k) {
                        while (st + 1 < e && xj(x(i), y(i ,x), x(q[e - 1]), y(q[e - 1],x), x(q[e - 2]), y(q[e - 2],x)) >= 0)
                            e--;

                        //
                        // dp[i] =  dp[j] + c[i] +  (s[i] - s[j])*x[i]   -    (px[i] - px[j])

                        //  dp[j] + px[j] =  s[j]*x[i] + dp[i] - c[i]  + px[i] - x[i]*s[i];


                        // dp[i] = t[i]*(c[i] - c[0]) + s * (c[n-1] -c[0]);
//                for(int j=0;j<i;++j){
//                    dp[i] = Math.min(dp[i] , dp[j] + c[i]2 + c[j]2 - 2c[i]*c[j] + s))

                        //                      dp[j] + c[j]2 = dp[i] -c[i]2 + 2*c[i] * c[j] -s;
//                    dp[i] = Math.min(dp[i] , dp[j] + (c[i]-c[j])*(t[i]) + s *(c[n-1] - c[j]));
//
//                }

//                dp[i] = dp[q[st]] + (c[i] - c[q[st]] + s);
//
//                while(st<e && dp[q[e-1]] -  c[q[e-1]] >= dp[i]  - c[i]) e--;
//
//                q[e++] = i;


//                while (st + 1 < e && (dp[q[st + 1]] + px[q[st+1]] - dp[q[st]] - px[q[st]]) <= (s[q[st + 1]] - s[q[st]]) * (x[i])) st++;
//
//                dp[i] =  dp[q[st]] + c[i] +  (s[i] - s[q[st]])*x[i]   -    (px[i] - px[q[st]]);

//                while (st + 1 < e && (s[i] - s[q[e - 1]]) * (dp[q[e - 1]]  + px[q[e-1]] - dp[q[e - 2]] - px[q[e-2]]) - (dp[i] + px[i] - dp[q[e - 1]] - px[q[e-1]]) * (s[q[e - 1]] - s[q[e - 2]]) >= 0)
//                    e--;
if(x>=2) {
    q[e++] = i;
}
           //         }

                }
            }
            out.println(dp[n+1][3]);

        }


        }


        static long mul(long a, long b, long p) {
            long res = 0, base = a;

            while (b > 0) {
                if ((b & 1L) > 0)
                    res = (res + base) % p;

                base = (base + base) % p;
                b >>= 1;
            }

            return res;
        }

        static long mod_pow(long k, long n, long p) {
            long res = 1L;
            long temp = k % p;

            while (n != 0L) {
                if ((n & 1L) == 1L) {
                    res = mul(res, temp, p);
                }

                temp = mul(temp, temp, p);
                n = n >> 1L;
            }

            return res % p;
        }


        public static double roundD(double result, int scale) {
            BigDecimal bg = new BigDecimal(result).setScale(scale, RoundingMode.UP);
            return bg.doubleValue();
        }



    }
    private static void solve() {
        InputStream inputStream = System.in;
//                InputStream inputStream  = null;
//                try {
//                    inputStream = new FileInputStream(new File("D:\\chrome_download\\exp.out"));
//                } catch (FileNotFoundException e) {
//                    e.printStackTrace();
//                }
        OutputStream outputStream = System.out;
//                OutputStream outputStream = null;
//                File f = new File("D:\\chrome_download\\");
//                try {
//                    f.createNewFile();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//                try {
//                    outputStream = new FileOutputStream(f);
//                } catch (FileNotFoundException e) {
//                    e.printStackTrace();
//                }
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(outputStream);
        Task task = new Task();
        task.solve(1, in, out);
        out.close();
    }
    public static void main(String[] args) {
        new Thread(null, () -> solve(), "1", (1 << 20)).start();
        // solve();
    }
    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;
        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }
        public String nextLine() {
            String line = null;

            try {
                line = reader.readLine();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return line;
        }
        public String next() {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
        public char nextChar() {
            return next().charAt(0);
        }
        public int[] nextArray(int n) {
            int res[] = new int[n];

            for (int i = 0; i < n; ++i) {
                res[i] = nextInt();
            }

            return res;
        }
        public long nextLong() {
            return Long.parseLong(next());
        }
        public double nextDouble() {
            return Double.parseDouble(next());
        }
    }
}

C++(g++ 7.5.0) 解法, 执行用时: 46ms, 内存消耗: 8260K, 提交时间: 2023-05-19 16:41:13

#include<bits/stdc++.h>
using namespace std;
const int N=2e5;
const int M=5e5;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
int n;
int l,r,pre,que[N+5];
long long x[N+5],sumw[N+5],sumx[N+5];
long long dp[N+5][2];
double slope(int x,int y){
	long long v=(dp[y][1]+sumx[y])-(dp[x][1]+sumx[x]);
	if(sumw[x]==sumw[y])return v>0?INF:-INF;
	return 1.0*v/(sumw[y]-sumw[x]);
}
int main(){
    ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		int X,Y;cin>>X>>Y;
		x[i]=x[i-1]+pre;pre=Y;
		sumw[i]=sumw[i-1]+X;
		sumx[i]=sumx[i-1]+1LL*X*x[i];
	}
	x[n+1]=x[n]+pre;
	sumw[n+1]=sumw[n];
	sumx[n+1]=sumx[n];
	++n;
	l=r=1;
	long long ans=1e18;
	for(int i=1;i<=n;i++){
		dp[i][1]=x[i]*sumw[i]-sumx[i];
		while(l<r && slope(que[l],que[l+1])<1.0*x[i])l++;
		if(i>1){
			int j=que[l];
			
			dp[i][2]=dp[j][1]+x[i]*(sumw[i]-sumw[j])-(sumx[i]-sumx[j]);
			ans=min(ans,dp[i][2]+x[n]*(sumw[n]-sumw[i])-(sumx[n]-sumx[i]));
			//cout<<i<<" "<<j<<" "<<dp[i][2]<<"\n";
		}
		while(l<r && slope(que[r-1],i)<slope(que[r-1],que[r]))r--;
		que[++r]=i;
	}
	cout<<ans;
	return 0;
}

C++(clang++11) 解法, 执行用时: 53ms, 内存消耗: 8184K, 提交时间: 2020-10-22 10:08:47

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,w[maxn],d[maxn],head,tail,line[maxn];
long long s1[maxn],s2[maxn],c[maxn],f[maxn],ans=2e18;
double getk(int a,int b){return double(s1[a]*s2[a]-s1[b]*s2[b])/double(s1[a]-s1[b]);}
inline long long all(int a,int b){return c[b]-c[a]-s1[a]*(s2[b]-s2[a]);}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){scanf("%d%d",&w[i],&d[i]);}
    n++;
    for(int i=1;i<n;i++){s1[i]=s1[i-1]+w[i];}
    for(int i=1;i<n;i++){s2[i+1]=s2[i]+d[i];}
    for(int i=1;i<=n;i++){c[i]=c[i-1]+s1[i-1]*d[i-1];}
    head=tail=1;
    for(int i=1;i<n;i++){
        while(head<tail&&getk(line[head+1],line[head])<s2[i]){head++;}
        f[i]=c[line[head]]+all(line[head],i)+all(i,n);
        while(head<tail&&getk(line[tail-1],i)>getk(line[tail],i))tail--;
        line[++tail]=i;
    }
    for(int i=2;i<n;i++){ans=min(ans,f[i]);}
    printf("%lld\n",ans);
	return 0;
}

上一题