各种OJ刷题记录5.31-6.8

各种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]线性代数

以前觉得挺神的题…推了推发现还是很水的。

容易发现:

D=i,jaiajBijiaici

由于 a {0,1}向量, Bij 有贡献当且仅当 ai=aj=1 ,而 ci 则在 ai=1 为时为负权,因此可以构造最大权闭合子图: Bij 为正权点, ci 为负权点, Bij 依赖 ci,cj ,然后套板子就好了。

#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学习了一个上下界最大最小流的姿势:

  1. 上下界最小流:先求不加入 TS 的最大流,记录为 F1 ,再在残量网络上加入 TS ,求最大流为 F2 。设辅助边容量和 C ,则F1+F2<C则无解,否则 F2 就是结果。
  2. 上下界最大流:先加入 TS ,跑一个 SSTT 的最大流;跑不满辅助边就无解。然后跑 ST 的最大流。结果就是 TS 上的流量。

这题需要当前弧一下,跑得比香港记者还快。

#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 天手上最多B X[i] A fi为第 i 天最多的钱,根据题目有:

YiRi=Xifi=YiBi+YiRiAi

然后找递推关系:

fi=maxj{YjBi+XjAi}fi=YjBi+XjAi=YiBi+XiAi

有:

Yj=AiBiXj+Yi+XiAiBi

后面的 Yi+XiAiBi=fiBi ,对于一大堆点要最大化 y 轴截距,则要维护一个上凸壳。然而这次斜率不随i单调,因此不能用单调队列,而要用 splay 维护凸包。

考虑用 cdq 分治解决这个问题。首先按斜率排序询问。过程 solve(l,r) 用来求解 [l,r] 内所有 fi ,并将 (xi,yi) 按横坐标排序。考虑先划分成 [l,mid] [mid+1,r] ;递归处理 [l,mid] 。然后得到了左边所有节点排好序的结果,用一个单调栈维护上凸壳。右边所有询问按斜率排序过了,直接扫一遍更新。然后就可以递归处理右边。

最后只剩一个问题,就是按 x 排序。这里只要用O(n)归并两个区间就可以了。

#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 次,问最大权值和。

首先用AC自动机找出所有的匹配位置(记录所有经过的位置,他在 fail 树上的祖先即是所有匹配位置),然后就变成了一个区间最大权 k 覆盖的模型。

然后就是网络流套路(类似志愿者招募那个模型)了:

  1. S1,n+1T,之间 ii+1 表示第 i 个位置,容量为x

    • 对于一个覆盖 [l,r,v] lr+1 ,流量为 1 ,费用为v
    • 考虑增广路的形态,显然由于从左向右连,不会有相交环干扰,每一条增广路都是不相交的环组成的。而流量 x 则限制了跨越他的环最多只有x个,而下面的流量 f0 ,所以存在一个 x+f 的割,根据最大流最小割定理, x+fx,f0xx ,满足了最多被覆盖 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;
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值