各种OJ刷题记录5.31-6.8
bzoj2733: [HNOI2012]永无乡
以前用启发式合并做的,突然脑补出了线段树合并做法,比启发式合并快到不知哪里去了。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m, k;
int l[MAXN*20], r[MAXN*20], lc[MAXN*20], rc[MAXN*20], dat[MAXN*20];
int top = 0;
int build(int opl, int opr, int pos)
{
int nd = ++top;
l[nd] = opl, r[nd] = opr, dat[nd] = 1;
if (opl < opr) {
int mid = (opl+opr)/2;
if (pos <= mid) lc[nd] = build(opl, mid, pos);
else rc[nd] = build(mid+1, opr, pos);
}
return nd;
}
int merge(int a, int b)
{
if (a==0||b==0) return a+b;
dat[a] += dat[b];
lc[a] = merge(lc[a], lc[b]);
rc[a] = merge(rc[a], rc[b]);
return a;
}
int kth(int nd, int k)
{
int l = 1, r = n;
while (l < r) {
int dt = dat[lc[nd]];
if (dt >= k) r = (l+r)/2, nd = lc[nd];
else k -= dt, l = (l+r)/2+1, nd = rc[nd];
}
return l;
}
int tr[MAXN];
int fa[MAXN];
inline int findf(int i)
{ return fa[i]?fa[i]=findf(fa[i]):i; }
int atp[MAXN];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int d; scanf("%d", &d);
atp[d] = i;
tr[i] = build(1, n, d);
}
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
u = findf(u), v = findf(v);
if (u != v) fa[u] = v, tr[v] = merge(tr[u], tr[v]);
}
scanf("%d", &k);
char str[3];
int u, v;
for (int i = 1; i <= k; i++) {
scanf("%s %d %d", str, &u, &v);
if (str[0] == 'B') {
u = findf(u), v = findf(v);
if (u != v) fa[u] = v, tr[v] = merge(tr[u], tr[v]);
} else {
u = findf(u);
if (dat[tr[u]] < v) puts("-1");
else printf("%d\n", atp[kth(tr[u], v)]);
}
}
return 0;
}
bzoj3996: [TJOI2015]线性代数
以前觉得挺神的题…推了推发现还是很水的。
容易发现:
由于
a
是
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 501*501+1005;
struct node {
int to, next, flow, neg;
} edge[MAXN*10];
int head[MAXN], top = 0;
inline void push(int i, int j, int f)
{
++top, edge[top] = (node){j, head[i], f, top+1}, head[i] = top;
++top, edge[top] = (node){i, head[j], 0, top-1}, head[j] = top;
}
int vis[MAXN], bfstime = 0;
int lev[MAXN];
int S = MAXN-3, T = MAXN-2;
queue<int> que;
bool bfs()
{
vis[S] = ++bfstime, lev[S] = 0, que.push(S);
while (!que.empty()) {
int nd = que.front(); que.pop();
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow;
if (vis[to] == bfstime || !f) continue;
vis[to] = bfstime, que.push(to), lev[to] = lev[nd]+1;
}
}
return vis[T] == bfstime;
}
int dfs(int nd, int flow)
{
if (nd == T || !flow) return flow;
int ans = 0, t;
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow;
if (lev[to] != lev[nd]+1 || !f) continue;
t = dfs(to, min(flow, f));
ans += t, edge[i].flow -= t;
edge[edge[i].neg].flow += t, flow -= t;
}
if (flow) lev[nd] = -1;
return ans;
}
int dinic()
{
int ans = 0;
while (bfs()) ans += dfs(S, 233333333);
return ans;
}
int B[505][505], n, c[MAXN];
int main()
{
scanf("%d", &n);
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &B[i][j]), ans += B[i][j];
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
int dat = (i-1)*n+j;
push(S, dat, B[i][j]);
push(dat, n*n+i, 233333333);
push(dat, n*n+j, 233333333);
}
for (int i = 1; i <= n; i++)
push(n*n+i, T, c[i]);
cout << ans-dinic() << endl;
return 0;
}
LYOI#158. 「上下界网络流」有源汇最小流
向Orz stdcall学习了一个上下界最大最小流的姿势:
- 上下界最小流:先求不加入
T→S
的最大流,记录为
F1
,再在残量网络上加入
T→S
,求最大流为
F2
。设辅助边容量和
C
,则
F1+F2<C 则无解,否则 F2 就是结果。 - 上下界最大流:先加入 T→S ,跑一个 SS→TT 的最大流;跑不满辅助边就无解。然后跑 S→T 的最大流。结果就是 T→S 上的流量。
这题需要当前弧一下,跑得比香港记者还快。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50010, MAXM = 100227*10;
struct node {
int to, next, flow, neg;
} edge[MAXM];
int head[MAXN], top = 0;
inline void push(int i, int j, int f)
{
++top, edge[top] = (node){j, head[i], f, top+1}, head[i] = top;
++top, edge[top] = (node){i, head[j], 0, top-1}, head[j] = top;
}
int SS = 50007, ST = 50008;
int inf = 2147483647;
int lev[MAXN], vis[MAXN], bfstime = 0;
queue<int> que;
int n, m, s, t;
bool bfs()
{
lev[SS] = 1, vis[SS] = ++bfstime;
que.push(SS);
while (!que.empty()) {
int nd = que.front(); que.pop();
for (int i = head[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow;
if (vis[to] == bfstime || !f) continue;
lev[to] = lev[nd]+1, que.push(to), vis[to] = bfstime;
}
}
return vis[ST] == bfstime;
}
int cur[MAXN];
long long dfs(int nd, int maxf)
{
if (nd == ST || !maxf) return maxf;
long long ans = 0;
int t;
for (register int &i = cur[nd]; i; i = edge[i].next) {
int to = edge[i].to, f = edge[i].flow;
if (lev[to] != lev[nd]+1 || !f) continue;
t = dfs(to, min(f, maxf));
ans += t, maxf -= t;
edge[i].flow -= t, edge[edge[i].neg].flow += t;
if (!maxf) break;
}
if (maxf) lev[nd] = -1;
return ans;
}
long long dinic()
{
long long ans = 0;
while (bfs()) {
for (int i = 1; i <= n; i++) cur[i] = head[i];
cur[SS] = head[SS], cur[ST] = head[ST];
ans += dfs(SS, inf);//, printf("%lld\n", ans);
}
return ans;
}
int main()
{
scanf("%d%d%d%d", &n, &m, &s, &t);
long long cnt = 0;
for (int i = 1; i <= m; i++) {
int u, v, l, r;
scanf("%d%d%d%d", &u, &v, &l, &r);
push(u, v, r-l), push(SS, v, l), push(u, ST, l);
cnt += l;
}
long long k = dinic();
push(t, s, inf);
long long ans = dinic();
if (k+ans < cnt) puts("please go home to sleep");
else cout << ans << endl;
return 0;
}
bzoj1492: [NOI2007]货币兑换Cash
好好做一个斜率优化…
首先是一个贪心性质,每次交易一定是 op=100% 的。
然后可以设
Y[i]
为第
i
天手上最多
然后找递推关系:
有:
后面的
Yi+XiAiBi=fiBi
,对于一大堆点要最大化
y
轴截距,则要维护一个上凸壳。然而这次斜率不随
考虑用 cdq 分治解决这个问题。首先按斜率排序询问。过程 solve(l,r) 用来求解 [l,r] 内所有 fi ,并将 (xi,yi) 按横坐标排序。考虑先划分成 [l,mid] 和 [mid+1,r] ;递归处理 [l,mid] 。然后得到了左边所有节点排好序的结果,用一个单调栈维护上凸壳。右边所有询问按斜率排序过了,直接扫一遍更新。然后就可以递归处理右边。
最后只剩一个问题,就是按
x
排序。这里只要用
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int n, m;
double f[MAXN];
const double eps = 1e-7, inf = 1e9;
struct query {
double a, b, r;
int pos;
inline double slop()
{
return -a/b;
}
} q[MAXN], tmp[MAXN];
struct point {
double x, y;
friend bool operator < (const point &a, const point &b)
{ return b.x-a.x>eps||(abs(b.x-a.x)<=eps && b.y-a.y > eps); }
} stk[MAXN], p[MAXN], tmp_p[MAXN];
int top;
inline double slop(const point &a, const point &b)
{
if (abs(a.x-b.x)<=eps) return -inf;
return (a.y-b.y)/(a.x-b.x);
}
bool cmp(query a, query b)
{ return a.slop() > b.slop(); }
void solve(int l, int r)
{
if (l == r) {
f[l] = max(f[l], f[l-1]);
p[l].y = f[l]/(q[l].a*q[l].r+q[l].b), p[l].x = q[l].r*p[l].y;
return;
}
int mid = (l+r)>>1, l1 = l, l2 = mid+1;
for (int i = l; i <= r; i++) {
if (q[i].pos <= mid) tmp[l1++] = q[i];
else tmp[l2++] = q[i];
}
for (int i = l; i <= r; i++)
q[i] = tmp[i];
solve(l, mid);
top = 0;
for (int i = l; i <= mid; i++) {
while (top >= 2 && slop(stk[top-1], stk[top]) < slop(stk[top], p[i])-eps) top--;
stk[++top] = p[i];
}
int lp = 1;
for (int i = mid+1; i <= r; i++) {
while (lp+1 <= top && slop(stk[lp], stk[lp+1]) > q[i].slop()-eps) lp++;
f[q[i].pos] = max(f[q[i].pos], stk[lp].x*q[i].a+stk[lp].y*q[i].b);
}
solve(mid+1, r);
int tp = l, a = l, b = mid+1;
while (a <= mid && b <= r) {
if (p[a] < p[b]) tmp_p[tp++] = p[a++];
else tmp_p[tp++] = p[b++];
}
while (a <= mid) tmp_p[tp++] = p[a++];
while (b <= r) tmp_p[tp++] = p[b++];
for (int i = l; i <= r; i++)
p[i] = tmp_p[i];
}
int main()
{
// freopen("cash.in", "r", stdin);
// freopen("cash.out", "w", stdout);
scanf("%d%lf", &n, &f[0]);
for (int i = 1; i <= n; i++) {
scanf("%lf%lf%lf", &q[i].a, &q[i].b, &q[i].r);
q[i].pos = i;
}
sort(q+1, q+n+1, cmp);
solve(1, n);
printf("%.3f\n", f[n]);
return 0;
}
cf717G. Underfail
题意:给定主串和模式串,每个模式串有权值,在某个位置匹配一次可以获取权值。主串每个位置最多被匹配 x 次,问最大权值和。
首先用
然后就是网络流套路(类似志愿者招募那个模型)了:
- 从
S→1,n+1→T ,之间 i→i+1 表示第 i 个位置,容量为x 。- 对于一个覆盖
[l,r,v]
,
l→r+1
,流量为
1
,费用为
v 考虑增广路的形态,显然由于从左向右连,不会有相交环干扰,每一条增广路都是不相交的环组成的。而流量 x 则限制了跨越他的环最多只有
x 个,而下面的流量 f≥0 ,所以存在一个 x+f 的割,根据最大流最小割定理, x′+f≤x,f≥0→x′≤x ,满足了最多被覆盖 x 次的限制。#include <bits/stdc++.h> using namespace std; int n, m, p[105], x; int len[105]; char str[505], s[505]; const int MAXN = 500*100; struct node { int to, next, flow, cost, neg; } edge[500*500]; int head[MAXN], tp = 0; inline void push(int i, int j, int f, int c) { ++tp, edge[tp] = (node){j, head[i], f, c, tp+1}, head[i] = tp; ++tp, edge[tp] = (node){i, head[j], 0, -c, tp-1}, head[j] = tp; } int dis[MAXN], vis[MAXN]; queue<int> q; int pre[MAXN], pre_edge[MAXN]; int S = MAXN-3, T = MAXN-2; bool spfa(int &flow, int &cost) { memset(dis, 127/3, sizeof dis); dis[S] = 0, q.push(S), vis[S] = 1; while (!q.empty()) { int nd = q.front(); q.pop(); vis[nd] = 0; // cerr << nd << endl; for (int i = head[nd]; i; i = edge[i].next) { int f = edge[i].flow, c = edge[i].cost; int to = edge[i].to; if (!f || dis[to] <= dis[nd]+c) continue; dis[to] = dis[nd]+c, pre[to] = nd, pre_edge[to] = i; if (!vis[to]) vis[to] = 1, q.push(to); } } if (dis[T] >= 233333333) return 0; int mf = INT_MAX; for (int i = T; i != S; i = pre[i]) mf = min(mf, edge[pre_edge[i]].flow); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].flow -= mf, edge[edge[pre_edge[i]].neg].flow += mf; flow += mf, cost += mf*dis[T]; return 1; } void mcf(int &flow, int &cost) { flow = cost = 0; while (spfa(flow, cost));// printf("...%d\n", cost); } void push_edge(int l, int r, int v) { push(l, r+1, 1, -v); } void push_init() { for (int i = 1; i <= n; i++) push(i, i+1, x, 0); push(S, 1, x, 0), push(n+1, T, x, 0); } int fail[MAXN], chl[MAXN][26], fin[MAXN]; vector<int> pos[MAXN]; int root = 0, top = 0; void push(int &nd, const char *str, int id) { if (!nd) nd = ++top; if (*str == '\0') fin[nd] = id; else push(chl[nd][*str-'a'], str+1, id); } queue<int> que; void init() { fail[root] = 0, que.push(root); while (!que.empty()) { int nd = que.front(); que.pop(); for (int i = 0; i < 26; i++) { if (!chl[nd][i]) continue; int p = fail[nd]; while (p && !chl[p][i]) p = fail[p]; if (p) fail[chl[nd][i]] = chl[p][i]; else fail[chl[nd][i]] = root; que.push(chl[nd][i]); } } } void match(int nd, const char *str, int ps) { for (int i = nd; i; i = fail[i]) if (fin[i]) push_edge(ps-len[fin[i]]+1, ps, p[fin[i]]); if (*str == '\0') return; while (nd && !chl[nd][*str-'a']) nd = fail[nd]; if (!nd) match(root, str+1, ps+1); else match(chl[nd][*str-'a'], str+1, ps+1); } int main() { scanf("%d", &n); scanf("%s", str); scanf("%d", &m); for (int i = 1; i <= m; i++) { scanf("%s %d", s, &p[i]), len[i] = strlen(s); push(root, s, i); } scanf("%d", &x); push_init(); init(); match(root, str, 0); int flow = 0, cost = 0; mcf(flow, cost); cout << -cost << endl; return 0; }
bzoj3622: 已经没有什么好害怕的了
广义容斥原理…
http://www.cnblogs.com/candy99/p/6617195.html#include <bits/stdc++.h> using namespace std; const int MAXN = 2005; int sug[MAXN], med[MAXN], n, k; int cnt[MAXN], fac[MAXN]; const int mod = 1e9+9; int f[MAXN][MAXN], a[MAXN]; int power(int a, int n) { int b = a, ans = 1; for (int i = 0; i <= 30; i++) { if (n&(1<<i)) ans = (long long)ans*b%mod; b = (long long)b*b%mod; } return ans; } int inv(int a) { return power(a, mod-2); } int choose(int n, int k) { return (long long)fac[n]*inv(fac[k])%mod*inv(fac[n-k])%mod; } int main() { scanf("%d%d", &n, &k); if ((n+k)&1) { printf("0\n"); return 0; } else k = (n+k)>>1; for (int i = 1; i <= n; i++) scanf("%d", &sug[i]); for (int i = 1; i <= n; i++) scanf("%d", &med[i]); fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = (long long)fac[i-1]*i%mod; sort(sug+1, sug+n+1), sort(med+1, med+n+1); int l = 0; for (int i = 1; i <= n; i++) { while (l < n && med[l+1] < sug[i]) l++; cnt[i] = l; } f[0][0] = 1; for (int i = 1; i <= n; i++) { for (int j = 0; j <= n; j++) { f[i][j] = f[i-1][j]; if (j>0) f[i][j] = (f[i][j]+(long long)f[i-1][j-1]*max(cnt[i]-j+1, 0)%mod)%mod; } } for (int i = 0; i <= n; i++) a[i] = (long long)f[n][i]*fac[n-i]%mod; int ans = 0; for (int i = k; i <= n; i++) ans = (ans+(((i-k)&1)?-1:1)*(long long)choose(i, k)*a[i]%mod)%mod; cout << (ans%mod+mod)%mod << endl; return 0; }
bzoj3339: Rmq Problem
常规方法不太好处理。
hzwer给出了一个思路:将询问按l 排序,用数据结构维护当前左端点下各个右端点的结果。然后计算每次移动左区间对后面值的影响。具体到这道题,首先用并查集或者树状数组上二分求出初始时的 ans[i]=mex{a[1]..a[i]} ,并预处理出每个值下一个这种值的位置 next[i] 。考虑 [l,n] 更新到 [l+1,n] 的影响就是将 l..next[l]−1 中所有大于 a[l] 的变成 a[l] ,也就是取 min 。这显然可以用线段树维护。
#include <bits/stdc++.h> using namespace std; const int MAXN = 200005, inf = 233333333; int fa[MAXN]; inline int findf(int nd) { return fa[nd]!=nd?fa[nd]=findf(fa[nd]):nd; } void link(int i, int j) { int a = findf(i), b = findf(j); if (a != b) fa[a] = b; } int a[MAXN], nxt[MAXN], now[MAXN]; int n, q; int mex[MAXN]; struct tree { int l[MAXN*4], r[MAXN*4], lc[MAXN*4], rc[MAXN*4], tag[MAXN*4], dat[MAXN*4]; int top, root; void clear() { top = root = 0; } void build(int &nd, int opl, int opr) { nd = ++top, l[nd] = opl, r[nd] = opr, tag[nd] = inf; if (opl < opr) build(lc[nd], opl, (opl+opr)>>1), build(rc[nd], ((opl+opr)>>1)+1, opr); else dat[nd] = mex[opl]; } void pdw(int nd) { if (tag[nd] == inf) return; if (lc[nd]) tag[lc[nd]] = min(tag[lc[nd]], tag[nd]), tag[rc[nd]] = min(tag[rc[nd]], tag[nd]), tag[nd] = inf; else dat[nd] = min(dat[nd], tag[nd]), tag[nd] = inf; } void modify(int nd, int opl, int opr, int dt) // 区间内大于dt的变为dt { if (opl > opr) return; pdw(nd); if (l[nd] == opl && r[nd] == opr) tag[nd] = min(tag[nd], dt); else { int mid = (l[nd]+r[nd])>>1; if (opr <= mid) modify(lc[nd], opl, opr, dt); else if (opl > mid) modify(rc[nd], opl, opr, dt); else modify(lc[nd], opl, mid, dt), modify(rc[nd], mid+1, opr, dt); } } int query(int nd, int dt) { pdw(nd); if (l[nd] == r[nd]) return dat[nd]; else { int mid = (l[nd]+r[nd])>>1; if (dt <= mid) return query(lc[nd], dt); else return query(rc[nd], dt); } } } seg; int last[MAXN]; struct qy { int l, r, id, ans; friend bool operator < (const qy &a, const qy &b) { return a.l < b.l; } } qs[MAXN]; int ans[MAXN]; int main() { scanf("%d%d", &n, &q); for (int i = 0; i <= n; i++) fa[i] = i; for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) { now[a[i]] = 1; if (a[i]-1>=0&&now[a[i]-1]) link(a[i]-1, a[i]); if (now[a[i]+1]) link(a[i], a[i]+1); mex[i] = now[0]?findf(0)+1:0; } // 并查集维护初始mex for (int i = 1; i <= n; i++) { if (last[a[i]]) nxt[last[a[i]]] = i; last[a[i]] = i; } for (int i = 1; i <= n; i++) if (nxt[i] == 0) nxt[i] = n+1; seg.build(seg.root, 1, n); // 初始值 for (int i = 1; i <= q; i++) scanf("%d%d", &qs[i].l, &qs[i].r), qs[i].id = i; sort(qs+1, qs+q+1); int now = 1; for (int i = 1; i <= n; i++) { while (now <= q && qs[now].l == i) { ans[qs[now].id] = seg.query(seg.root, qs[now].r); now++; } seg.modify(seg.root, i, nxt[i]-1, a[i]); } for (int i = 1; i <= q; i++) printf("%d\n", ans[i]); return 0; }
COGS2648.[IOI2011]Race
1A了…感慨万千…
OI中摩尔定律:知识量随时间以指数增长,题目难度随时间以指数降低。
#include <bits/stdc++.h> using namespace std; const int MAXN = 200005; struct node { int to, next, dis; } edge[MAXN*2]; int head[MAXN], top = 0; void push(int i, int j, int d) { edge[++top] = (node){j, head[i], d}, head[i] = top; } int siz[MAXN], vis[MAXN]; void dfs_siz(int nd, int f) { siz[nd] = 1; for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; dfs_siz(to, nd), siz[nd] += siz[to]; } } void dfs_sel(int nd, int f, const int totsiz, int &ans, int &maxl) { int maxs = 0; if (f) maxs = totsiz-siz[nd]; for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; maxs = max(maxs, siz[to]); } if (maxs < maxl) maxl = maxs, ans = nd; for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; dfs_sel(to, nd, totsiz, ans, maxl); } } int sig[1000005]; int n, k, pans = 233333333, cnt = 233333333; void dfs_calc(int nd, int f, int dep, int now_len) { if (now_len > k) return; cnt = min(cnt, sig[k-now_len]+dep); for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; dfs_calc(to, nd, dep+1, now_len+edge[i].dis); } } void dfs_mod(int nd, int f, int dep, int now_len) { if (now_len > k) return; sig[now_len] = min(sig[now_len], dep); for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; dfs_mod(to, nd, dep+1, now_len+edge[i].dis); } } void dfs_dec(int nd, int f, int dep, int now_len) { if (now_len > k) return; sig[now_len] = 233333333; for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to; if (to == f || vis[to]) continue; dfs_dec(to, nd, dep+1, now_len+edge[i].dis); } } void calc(int nd) { dfs_siz(nd, 0); int center = 0, ans = 233333333; dfs_sel(nd, 0, siz[nd], center, ans); // cerr << center << endl; vis[center] = 1, cnt = 233333333, sig[0] = 0; for (int i = head[center]; i; i = edge[i].next) { int to = edge[i].to, d = edge[i].dis; if (vis[to]) continue; dfs_calc(to, 0, 1, d), dfs_mod(to, 0, 1, d); // cerr << to << endl; // for (int j = 1; j <= k; j++) // cerr << sig[j] << " "; // cerr << endl << cnt << endl; } pans = min(pans, cnt); // cerr << "gg" << ans << endl; dfs_dec(center, 0, 0, 0); for (int i = head[center]; i; i = edge[i].next) { int to = edge[i].to; if (vis[to]) continue; calc(to); } } int main() { freopen("ioi2011-race.in", "r", stdin); freopen("ioi2011-race.out", "w", stdout); int size = 128 << 20; char *p = (char*)malloc(size) + size; __asm__("movl %0, %%esp\n" :: "r"(p)); memset(sig, 127/3, sizeof sig); scanf("%d%d", &n, &k); for (int i = 1; i < n; i++) { int u, v, d; scanf("%d%d%d", &u, &v, &d), u++, v++; push(u, v, d), push(v, u, d); } calc(1); if (pans < 2333333) cout << pans << endl; else puts("-1"); return 0; }
bzoj2879: [Noi2012]美食节
学习了一个动态加边。
#include <bits/stdc++.h> using namespace std; const int MAXN = 40+80*100+5; struct node { int to, next, flow, cost, neg; } edge[MAXN*20]; int head[MAXN], top = 0; void push(int i, int j, int f, int c) { // printf("%d--%d,%d-->%d\n", i, f, c, j); ++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top; ++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top; } int dis[MAXN], vis[MAXN], pre[MAXN], pre_edge[MAXN]; queue<int> que; int S = MAXN-5, T = MAXN-4; int inf = 23333333; bool spfa(int &flow, int &cost) { memset(dis, 127/3, sizeof dis); memset(vis, 0, sizeof vis); vis[S] = 1, que.push(S), dis[S] = 0; while (!que.empty()) { int nd = que.front(); que.pop(); vis[nd] = 0; // cerr << nd << endl; for (int i = head[nd]; i; i = edge[i].next) { int to = edge[i].to, f = edge[i].flow, c = edge[i].cost; if (!f || dis[to] <= dis[nd]+c) continue; dis[to] = dis[nd]+c; pre[to] = nd, pre_edge[to] = i; if (!vis[to]) vis[to] = 1, que.push(to); } } // cerr << dis[T] << endl; if (dis[T] > inf) return 0; int maxf = inf; for (int i = T; i != S; i = pre[i]) maxf = min(maxf, edge[pre_edge[i]].flow); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].flow -= maxf, edge[edge[pre_edge[i]].neg].flow += maxf; flow += maxf, cost += maxf*dis[T]; return 1; } int id[MAXN], k[MAXN], dish[MAXN], n, m; int top_tp = 0; int t[45][505], p[45]; void mcf(int &flow, int &cost) { flow = cost = 0; while (spfa(flow, cost)) { int x = pre[T]; // cerr << "id " << x << endl; x = id[x]; k[x]++, id[++top_tp] = x; for (int i = 1; i <= n; i++) push(dish[i], top_tp, 1, k[x]*t[i][x]); push(top_tp, T, 1, 0); // printf("---%d\n", flow); } } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &p[i]); for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) scanf("%d", &t[i][j]); for (int i = 1; i <= n; i++) dish[i] = ++top_tp; for (int i = 1; i <= n; i++) push(S, dish[i], p[i], 0); for (int i = 1; i <= m; i++) { id[++top_tp] = i, k[i]++; for (int j = 1; j <= n; j++) push(dish[j], top_tp, 1, k[i]*t[j][i]); push(top_tp, T, 1, 0); } int flow, cost; // puts("---"); mcf(flow, cost); cout << cost << endl; return 0; }
- 对于一个覆盖
[l,r,v]
,
l→r+1
,流量为
1
,费用为