//需要注意的是,因为利用了位运算的奇技淫巧,所以树状数组需要从1开始 classNumArray { public: //树状数组,https://oi-wiki.org/ds/fenwick/ vector<int>bit;//bit[1....n] bit[1]表示a[0];bit[2]表示a[0]+a[1]; 管辖的区间是[x-lowbit(x),x-1]; 假设管辖的数组从0开始计数 intlowbit(int x){ // lowbit(0b01011000) == 0b00001000 return x & -x; } // bit[0]..bit[x] 中间覆盖的点的和 相当于nums[0]+...+nums[x] intpre_sum(int x){ int sum = 0; while(x >= 0){ if(x==0){ sum += bit[x]; break; } sum += bit[x]; x -= lowbit(x); } return sum; } //单点加法,对于x加上y,更新所有的树状数组 voidadd(int x, int y){ while(x < bit.size()){ bit[x] += y; x += lowbit(x); } } //bit[left] 到 bit[right] 中间覆盖的点的和 nums[left]+...+nums[right] intget_sum(int left,int right){ if(left>0) return pre_sum(right) - pre_sum(left-1); else{ return pre_sum(right); } } NumArray(vector<int>& nums) { int n=nums.size(); bit.resize(n,0); for(int i=0;i<n;i++){ bit[i]+=nums[i]; int t=i+lowbit(i);//树状数组性质:c[x]真包含于c[x+lowbit(x)] if(t<n)bit[t]+=bit[i]; } } voidupdate(int index, int val){ int old = sumRange(index, index); add(index, val-old); } intsumRange(int left, int right){ return get_sum(left, right);
} };
/** * Your NumArray object will be instantiated and called as such: * NumArray* obj = new NumArray(nums); * obj->update(index,val); * int param_2 = obj->sumRange(left,right); */
SegmentTreeNode*buildTree(int start,int end){ if(start==end){ returnnew SegmentTreeNode(start,end,_nums[start]); } int mid=start+(end-start)/2; auto left=buildTree(start,mid); auto right=buildTree(mid+1,end); auto node=new SegmentTreeNode(start, end, left->sum + right->sum, left, right); return node; } voidupdateTree(SegmentTreeNode*root,int i,int val){ if(root->start==i&&root->end==i){ root->sum=val; return; } int mid=root->start+(root->end-root->start)/2; if(i<=mid){ updateTree(root->left,i,val); }else{ updateTree(root->right,i,val); } root->sum=root->left->sum+root->right->sum; } intsumRange(SegmentTreeNode*root,int i ,int j){ if(i==root->start&&j==root->end){ return root->sum; } int mid=root->start+(root->end-root->start)/2; if(j<=mid){ return sumRange(root->left,i,j); }elseif (i >mid){ return sumRange(root->right,i,j); }else{ return sumRange(root->left,i,mid)+sumRange(root->right,mid+1,j); } } };
/** * Your NumArray object will be instantiated and called as such: * NumArray* obj = new NumArray(nums); * obj->update(index,val); * int param_2 = obj->sumRange(left,right); */
vector<vector<int> > g; vector<int > x; vector<int > y; vector<bool > vis; int n, m;
boolhungary(int u){ // u为X部的顶点 // v为Y部的顶点 for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (vis[v]) { continue; } vis[v] = true; // 如果Y部的顶点v还未被匹配,那么可以去匹配它 // 假设v匹配了X部的顶点p,且从p能找到另一个匹配q,那么x-y-p-q是一条增广路径 if (y[v] == -1 || hungary(y[v])) { x[u] = v; y[v] = u; returntrue; } } returnfalse; }
intmain(){ while (cin >> n >> m) { g = vector<vector<int> >(n, vector<int>(m, 0)); vis = vector<bool >(m, false); x = vector<int >(n, -1); y = vector<int >(m, -1);
for (int i = 0; i < n; i++) { int x; cin >> x; while (x--) { int a; cin >> a; g[i].push_back(a - 1); } }
int res = 0; for (int u = 0; u < n; u++) { // 如果X部还存在点未被匹配,那么尝试去匹配它 if (x[u] == -1) { vis = vector<bool >(m + 1, false); if (hungary(u)) { res++; } } }
structEdge { int u; int v; int cap; int cost; int next; Edge() = default; Edge(int _u, int _v, int _cap, int _cost, int _n) : u(_u), v(_v), cap(_cap), cost(_cost), next(_n) {} };
structGraph { vector<int> head; vector<Edge> edges; Graph() = delete; Graph(int n) : head(vector<int>(n, -1)) {} voidAddEdge(int u, int v, int cap, int cost){ edges.emplace_back(u, v, cap, cost, head[u]); head[u] = edges.size() - 1; } };
intspfa(int source, int dest, int n, Graph& g, vector<int>& pre, vector<int>& path, vector<int>& costSum){ constexprint inf = 0x3fffffff; queue<int> q; vector<bool> inque(n ,false); pre = vector<int>(n, -1); path = vector<int>(n, -1); costSum = vector<int>(n, inf); vector<int> flow(n, 0);
// 源点的流量初始为无穷大,cost为0 q.push(source); inque[source] = true; flow[source] = inf; costSum[source] = 0; while (!q.empty()) { int u = q.front(); q.pop(); // 这里是spfa求最短路,不完全是一遍bfs inque[u] = false;
for (int eid = g.head[u]; eid != -1; eid = g.edges[eid].next) { int v = g.edges[eid].v; int cost = g.edges[eid].cost; int cap = g.edges[eid].cap; // 松弛边 if (g.edges[eid].cap > 0 && costSum[v] > costSum[u] + cost) { // 更新到v点的最小cost之和 costSum[v] = costSum[u] + cost; // 更新到v点的最大流量 flow[v] = min(flow[u], cap); // 记录到v点的上一个点和边号 pre[v] = u; path[v] = eid; // spfa算法中v重新入队 if (!inque[v]) { inque[v] = true; q.push(v); } } } }
return flow[dest]; }
intmcmf(int source, int dest, int n, Graph& g){ vector<int> pre(n, -1); vector<int> path(n, -1); vector<int> costSum(n, 0);
int minCost = 0; int maxFlow = 0; while (true) { // 运行一次最短路,求出增广路径上的流量 int flow = spfa(source, dest, n, g, pre, path, costSum); if (flow == 0) { break; } // 更新最大流量和最小费用 maxFlow += flow; minCost += flow * costSum[dest]; // 更新残差网络 int cur = dest; while (cur != source) { g.edges[path[cur]].cap -= flow; g.edges[path[cur] ^ 1].cap += flow; cur = pre[cur]; } }
cout << maxFlow << " " << minCost << endl;
return minCost; }
intmain(){ int n, m, s, t; cin >> n >> m >> s >> t; Graph g(n); while (m--) { int a, b, c, d; cin >> a >> b >> c >> d; // 正向边,capacity为c,cost为d g.AddEdge(a-1, b-1, c, d); // 反向边,capacity为0,cost为-d g.AddEdge(b-1, a-1, 0, -d); } mcmf(s-1, t-1, n, g); return0; }