@ljt12138
2017-06-09T19:30:46.000000Z
字数 16497
阅读 806
以前用启发式合并做的,突然脑补出了线段树合并做法,比启发式合并快到不知哪里去了。
#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;
}
以前觉得挺神的题...推了推发现还是很水的。
容易发现:
由于是向量,有贡献当且仅当,而则在为时为负权,因此可以构造最大权闭合子图:为正权点,为负权点,依赖,然后套板子就好了。
#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;
}
向Orz stdcall学习了一个上下界最大最小流的姿势:
这题需要当前弧一下,跑得比香港记者还快。
#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;
}
好好做一个斜率优化...
首先是一个贪心性质,每次交易一定是的。
然后可以设为第天手上最多,为,为第天最多的钱,根据题目有:
然后找递推关系:
有:
后面的,对于一大堆点要最大化轴截距,则要维护一个上凸壳。然而这次斜率不随单调,因此不能用单调队列,而要用维护凸包。
考虑用分治解决这个问题。首先按斜率排序询问。过程用来求解内所有,并将按横坐标排序。考虑先划分成和;递归处理。然后得到了左边所有节点排好序的结果,用一个单调栈维护上凸壳。右边所有询问按斜率排序过了,直接扫一遍更新。然后就可以递归处理右边。
最后只剩一个问题,就是按排序。这里只要用归并两个区间就可以了。
#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;
}
题意:给定主串和模式串,每个模式串有权值,在某个位置匹配一次可以获取权值。主串每个位置最多被匹配次,问最大权值和。
首先用自动机找出所有的匹配位置(记录所有经过的位置,他在树上的祖先即是所有匹配位置),然后就变成了一个区间最大权覆盖的模型。
然后就是网络流套路(类似志愿者招募那个模型)了:
考虑增广路的形态,显然由于从左向右连,不会有相交环干扰,每一条增广路都是不相交的环组成的。而流量则限制了跨越他的环最多只有个,而下面的流量,所以存在一个的割,根据最大流最小割定理,,满足了最多被覆盖次的限制。
#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;
}
广义容斥原理...
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;
}
常规方法不太好处理。
hzwer给出了一个思路:将询问按排序,用数据结构维护当前左端点下各个右端点的结果。然后计算每次移动左区间对后面值的影响。
具体到这道题,首先用并查集或者树状数组上二分求出初始时的,并预处理出每个值下一个这种值的位置。考虑更新到的影响就是将中所有大于的变成,也就是取。这显然可以用线段树维护。
#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;
}
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;
}
学习了一个动态加边。
#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;
}