ACdream 1103 瑶瑶正式成为CEO(树链剖分+费用流)

Problem Description

瑶瑶(tsyao)是某知名货运公司(顺丰)的老板,这个公司很大,货物运输量极大,因此公司修建了许多交通设施,掌控了一个国家的交通运输。

这个国家有n座城市,公司的总部在1号城市。

公司下管辖的有m条道路和n-1段火车线路。

这m条道路和n-1条火车线路都可以用u来表示起点,v来表示终点(数据保证m条道路和n-1条火车线路构成有向无环图)。

这n-1段火车道保证从1号城市出发,能够有唯一的一道只包含火车道的线路到达其他n-1座城市。

每条道路和每段火车道都有一个最佳搭载货物值ai,如果要搭载的货物超过了ai,公司需要对经过该路线的超过ai的每单位货物增加bi的维修费用,由于种种原因,如果搭载的货物低于ai,公司需要对少的每单位货物(设该路线有x的货物,则计算为ai-x单位)增加ci的维修费用。当然,每单位货物经过时,会有di的基础维护费用。

这里有两种操作:

C xi yi zi: 随着时间和环境的变化,火车道会受到一些影响,xi到yi的火车道ai会改变zi(新的ai应该为ai+zi),若xi不能到达yi,则将hi到xi和hi到yi的路段ai改变zi(hi为可以到达xi和yi的城市,且不存在fi使得  hi能够到达fi,fi能够到达xi和yi)。

当某火车道的ai值小于0时,我们看做该条火车道的最佳搭载货物值为0(当然ai事实上是负数);

Q vi wi: 查询当计划将wi单位货物从1号城市到vi号城市时,该公司需要的最小维护费用。维护费用计算为每条道路和火车道的维护费用的和(SUMbi+SUMci+SUMdi)。

Input

第一行两个整数n,m,用空格隔开。

接下来n-1+m行,每行u,v,ai,bi,ci,di六个整数。

前n-1行表示火车线路,后m行表示道路。

接下来一行为一个整数q。

接下来q行分别为上述两种操作。

Output

对于每次Q操作,输出答案,数据保证答案在int范围内。

题目大意:略。

思路:

——————————————————————————————————————————————————————————————————

扒官方题解:http://tsyao.tk/archives/94

对付修改的话,就用树链剖分就好,然后每次询问跑网络流。

网络流这样建图,先假设我每条边都跑了0的流量,我们先算出跑了0的流量的费用,然后对于一条边,我跑了小于ai的流量的时候,每次增加一点流量,就相当于减小了ci的费用,所以把一条边拆成两条边,一条是费用为-ci+di,上界为ai的边,一条是费用为bi+di,上界为无穷的边。。。

——————————————————————————————————————————————————————————————————

PS:简单的说就是两条SB题合在一起出,这样也脱离不了它是SB题的结果。但是却忘了删一条调试语句导致无限TLE……

代码(1772MS):

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 using namespace std;
  7 typedef long long LL;
  8
  9 const int MAXN = 510;
 10 const int MAXV = MAXN;
 11 const int MAXE = 2 * (2010 * 2 + MAXN * 2);
 12 const int MAXT = MAXN << 2;
 13 const int INF = 0x3f3f3f3f;
 14
 15 struct SPFA_COST_FLOW {
 16     int head[MAXV];
 17     int to[MAXE], next[MAXE], cap[MAXE], flow[MAXE];
 18     LL cost[MAXE];
 19     int n, m, ecnt, st, ed;
 20
 21     void init(int n) {
 22         this->n = n;
 23         memset(head + 1, -1, n * sizeof(int));
 24         ecnt = 0;
 25     }
 26
 27     void add_edge(int u, int v, int f, LL c) {
 28         to[ecnt] = v; cap[ecnt] = f; cost[ecnt] = c; next[ecnt] = head[u]; head[u] = ecnt++;
 29         to[ecnt] = u; cap[ecnt] = 0; cost[ecnt] = -c; next[ecnt] = head[v]; head[v] = ecnt++;
 30     }
 31
 32     void clear() {
 33         memset(flow, 0, ecnt * sizeof(int));
 34     }
 35
 36     LL dis[MAXV];
 37     int pre[MAXV];
 38     bool vis[MAXV];
 39
 40     bool spfa() {
 41         memset(vis + 1, 0, n * sizeof(bool));
 42         memset(dis, 0x3f, (n + 1) * sizeof(LL));
 43         queue<int> que; que.push(st);
 44         dis[st] = 0;
 45         while(!que.empty()) {
 46             int u = que.front(); que.pop();
 47             vis[u] = false;
 48             for(int p = head[u]; ~p; p = next[p]) {
 49                 int v = to[p];
 50                 if(cap[p] - flow[p] && dis[u] + cost[p] < dis[v]) {
 51                     dis[v] = dis[u] + cost[p];
 52                     pre[v] = p;
 53                     if(!vis[v]) {
 54                         que.push(v);
 55                         vis[v] = true;
 56                     }
 57                 }
 58             }
 59         }
 60         return dis[ed] < dis[0];
 61     }
 62
 63     LL maxFlow, minCost;
 64
 65     LL min_cost_flow(int ss, int tt) {
 66         st = ss, ed = tt;
 67         maxFlow = minCost = 0;
 68         while(spfa()) {
 69             int u = ed, tmp = INF;
 70             while(u != st) {
 71                 tmp = min(tmp, cap[pre[u]] - flow[pre[u]]);
 72                 u = to[pre[u] ^ 1];
 73             }
 74             u = ed;
 75             while(u != st) {
 76                 flow[pre[u]] += tmp;
 77                 flow[pre[u] ^ 1] -= tmp;
 78                 u = to[pre[u] ^ 1];
 79             }
 80             maxFlow += tmp;
 81             minCost += tmp * dis[ed];
 82         }
 83         return minCost;
 84     }
 85 } G;
 86
 87 struct Tree {
 88     struct Edge {
 89         int a, b, c, d, u, v;
 90         void read() {
 91             scanf("%d%d%d%d%d%d", &u, &v, &a, &b, &c, &d);
 92         }
 93     } tree[MAXN], edge[MAXE];
 94     int tid[MAXN], eid[MAXN], size[MAXN], son[MAXN], top[MAXN], dep[MAXN], fa[MAXN];
 95     int n, m, q;
 96
 97     int head[MAXV], pre[MAXV], ecnt, dfs_clock;
 98     int to[MAXE], next[MAXE], id[MAXE];
 99     LL add[MAXT];
100
101     void init() {
102         memset(head + 1, -1, n * sizeof(int));
103         ecnt = dfs_clock = 0;
104     }
105
106     void add_edge(int u, int v, int i) {
107         to[ecnt] = v; id[ecnt] = i; next[ecnt] = head[u]; head[u] = ecnt++;
108         to[ecnt] = u; id[ecnt] = i; next[ecnt] = head[v]; head[v] = ecnt++;
109     }
110
111     void dfs_size(int u, int f, int depth) {
112         size[u] = 1; dep[u] = depth; fa[u] = f;
113         int maxsize = son[u] = 0;
114         for(int p = head[u]; ~p; p = next[p]) {
115             int v = to[p];
116             if(v == f) continue;
117             pre[v] = p;
118             dfs_size(v, u, depth + 1);
119             size[u] += size[v];
120             if(size[v] > maxsize) {
121                 maxsize = size[v];
122                 son[u] = v;
123             }
124         }
125     }
126
127     void dfs_heavy_edge(int u, int ancestor) {
128         tid[u] = ++dfs_clock; eid[dfs_clock] = id[pre[u]];
129         top[u] = ancestor;
130         if(son[u]) dfs_heavy_edge(son[u], ancestor);
131         for(int p = head[u]; ~p; p = next[p]) {
132             int v = to[p];
133             if(v == fa[u] || v == son[u]) continue;
134             dfs_heavy_edge(v, v);
135         }
136     }
137
138     #define ll (x << 1)
139     #define rr (ll | 1)
140     #define mid ((l + r) >> 1)
141
142     void pushdown(int x) {
143         if(add[x]) {
144             add[ll] += add[x];
145             add[rr] += add[x];
146             add[x] = 0;
147         }
148     }
149
150     void pushadd(int x, int l, int r) {
151         if(l == r) {
152             if(l > 1) tree[eid[l]].a += add[x];
153             add[x] = 0;
154         } else {
155             pushdown(x);
156             pushadd(ll, l, mid);
157             pushadd(rr, mid + 1, r);
158         }
159     }
160
161     void modify(int x, int l, int r, int a, int b, int val) {
162         if(a <= l && r <= b) {
163             add[x] += val;
164         } else {
165             if(a <= mid) modify(ll, l, mid, a, b, val);
166             if(mid < b) modify(rr, mid + 1, r, a, b, val);
167         }
168     }
169
170     void modify(int x, int y, int val) {
171         while(top[x] != top[y]) {
172             if(dep[top[x]] < dep[top[y]]) swap(x, y);
173             modify(1, 1, n, tid[top[x]], tid[x], val);
174             x = fa[top[x]];
175         }
176         if(x != y) {
177             if(dep[x] < dep[y]) swap(x, y);
178             modify(1, 1, n, tid[son[y]], tid[x], val);
179         }
180     }
181
182     int gid[MAXV];
183     void initQuery() {
184         G.init(n + 1);
185         for(int i = 1; i < n; ++i) {
186             Edge &t = tree[i];
187             gid[i] = G.ecnt;
188             G.add_edge(t.u, t.v, max(0, t.a), t.d - t.c);
189             G.add_edge(t.u, t.v, INF, t.d + t.b);
190         }
191         for(int i = 0; i < m; ++i) {
192             Edge &t = edge[i];
193             G.add_edge(t.u, t.v, max(0, t.a), t.d - t.c);
194             G.add_edge(t.u, t.v, INF, t.d + t.b);
195         }
196         G.add_edge(n + 1, 1, 0, 0);
197     }
198
199     int query(int tt, int val) {
200         int ss = n + 1;
201         pushadd(1, 1, n);
202         LL sum = 0;
203         for(int i = 1; i < n; ++i) {
204             Edge &t = tree[i];
205             sum += t.c * max(0, t.a);
206             G.cap[gid[i]] = max(0, t.a);
207         }
208         for(int i = 0; i < m; ++i) {
209             Edge &t = edge[i];
210             sum += t.c * max(0, t.a);
211         }
212         G.cap[G.ecnt - 2] = val;
213         G.clear();
214         return sum + G.min_cost_flow(ss, tt);
215     }
216
217     void work() {
218         scanf("%d%d", &n, &m);
219         for(int i = 1; i < n; ++i) tree[i].read();
220         for(int i = 0; i < m; ++i) edge[i].read();
221         init();
222         initQuery();
223         for(int i = 1; i < n; ++i) add_edge(tree[i].u, tree[i].v, i);
224         dfs_size(1, 0, 1);
225         dfs_heavy_edge(1, 1);
226         scanf("%d", &q);
227         char op;
228         for(int i = 0, a, b, c; i < q; ++i) {
229             scanf(" %c", &op);
230             if(op == ‘Q‘) {
231                 scanf("%d%d", &a, &b);
232                 printf("%d\n", query(a, b));
233             } else {
234                 scanf("%d%d%d", &a, &b, &c);
235                 modify(a, b, c);
236             }
237         }
238     }
239 } T;
240
241 int main() {
242     T.work();
243 }

ACdream 1103 瑶瑶正式成为CEO(树链剖分+费用流)

时间: 2024-10-06 14:13:36

ACdream 1103 瑶瑶正式成为CEO(树链剖分+费用流)的相关文章

BZOJ 1103: [POI2007]大都市meg( 树链剖分 )

早上数学考挂了...欲哭无泪啊下午去写半个小时政治然后就又可以来刷题了.. 树链剖分 , 为什么跑得这么慢... --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #define rep( i , n ) for( int

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In

bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 7925  Solved: 2975[Submit][Status][Discuss] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的

洛谷 P3384 【模板】树链剖分

题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,分别表示树的结点个数.操作个数

bzoj1036 树的统计(树链剖分+线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 15120  Solved: 6141[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

SPOJ QTREE Query on a tree ——树链剖分 线段树

[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 20005 int T,n,fr[maxn],h[maxn],to[maxn],ne[maxn]

树链剖分简(单)介(绍)

树链剖分可以算是一种数据结构(一大堆数组,按照这个意思,主席树就是一大堆线段树).将一棵树分割成许多条连续的树链,方便完成一下问题: 单点修改(dfs序可以完成) 求LCA(各种乱搞也可以) 树链修改(修改任意树上两点之间的唯一路径) 树链查询 (各种操作)  前两个内容可以用其他方式解决,但是下面两种操作倍增.st表,dfs序就很难解决(解决当然可以解决,只是耗时长点而已).下面开始步入正题. 树链剖分的主要目的是分割树,使它成一条链,然后交给其他数据结构(如线段树,Splay)来进行维护.常

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<