@description@

给定序列 A,序列中的每一项 Ai 有删除代价 Bi 和附加属性 Ci

请删除若干项,使得 A 的最长上升子序列长度减少至少 1,且付出的代价之和最小,并输出方案。

如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。

输入格式

输入包含多组数据。

输入的第一行包含整数 T,表示数据组数。

接下来 4T 行描述每组数据:

每组数据的第一行包含一个整数 N,表示 A 的项数,接下来三行,每行 N 个整数 A1, ..., AN; B1, ..., BN; C1, ..., CN。

输出格式

对每组数据,输出两行。

第一行包含两个整数 S,M,依次表示删去项的代价和与数量;

接下来一行 M 个整数,表示删去项在 A 中的位置,按升序输出。

样例

样例输入

1

6

3 4 4 2 2 3

2 1 1 1 1 2

6 5 4 3 2 1

样例输出

4 3

2 3 6

数据范围与提示

对于所有的数据, 1 <= N <= 700, T <= 5, 1 <= Ai, Bi, Ci <= 10^9 且保证 Ci 两两不同。

@solution@

假如不考虑方案,我们可以先跑最长上升子序列的 dp,记录每个点为结尾的最长上升子序列 f[x],并记录全局最优解 mx。

然后考虑建一个分层图,其中 x 向 y 连边当且仅当 dp 时 x 能够作为 y 的最优决策点(即 x < y 且 A[x] < A[y] 且 f[x] + 1 = f[y])。

假如我选择一些点删掉,使得整张图没有点可以从 f[x] = 1 跑到 f[x] = mx,则原序列的最长上升子序列肯定长度减小。

于是我们考虑先将建出来的分层图拆点,然后建源点 s 连 f[x] = 1 的点,将 f[x] = mx 的点连向汇点 t,跑最小割即可得到答案。

现在来考虑怎么求一个字典序最小的最小割方案。不难想到应该使用贪心的方法,按 Ci 从小到大依次取,并尝试将当前这个 i 加入最小割。

怎么判断一条边 i 是否可以在最小割内?最暴力的方法无疑是将这条边删掉,看最大流(即最小割)的变化量是否等于这条边的容量。

等效替代一下,就是看这条边 (u, v) 是否能从 u 出发通过残余网络到达 v。

怎么消掉一条边的影响?最暴力的方法一样是将这条边删掉,然后重跑最大流。

稍微快一些的方法是:跑 (t, v) 的最大流,跑 (u, s) 的最大流,跑最大流时限制总流量 = (u, v) 的流量,并把 (u, v) 这条边的容量与流量都设置为 0。

这个操作称之为退流操作。

然而退流操作要常数小,有时候单路增广甚至要快一些(因为增广路本身不会太长),而多路增广需要重新给 n 个点求距离标号所以会大量访问到无用点。

比较折中方法是在 dinic 求距离标号时一旦遇到终点就立刻返回进行增广。

@accepted code@

  1. #include<cstdio>
  2. #include<vector>
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<queue>
  6. using namespace std;
  7. const int MAXN = 700;
  8. const int MAXV = 2*MAXN;
  9. const int MAXE = 3*MAXV*MAXV;
  10. const int INF = (1<<30);
  11. struct FlowGraph{
  12. struct edge{
  13. int to, cap, flow;
  14. edge *nxt, *rev;
  15. }edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
  16. int s, t, n;
  17. void clear(int _n) {
  18. n = _n; ecnt = &edges[0];
  19. for(int i=0;i<=n;i++)
  20. adj[i] = NULL;
  21. }
  22. void addedge(int u, int v, int c) {
  23. edge *p = (++ecnt), *q = (++ecnt);
  24. p->to = v, p->cap = c, p->flow = 0;
  25. p->nxt = adj[u], adj[u] = p;
  26. q->to = u, q->cap = 0, q->flow = 0;
  27. q->nxt = adj[v], adj[v] = q;
  28. p->rev = q, q->rev = p;
  29. // printf("%d %d %lld\n", u, v, c);
  30. }
  31. queue<int>que; int d[MAXV + 5];
  32. bool relabel() {
  33. for(int i=0;i<=n;i++)
  34. cur[i] = adj[i], d[i] = n + 5;
  35. while( !que.empty() ) que.pop();
  36. d[t] = 0, que.push(t);
  37. while( !que.empty() ) {
  38. int f = que.front(); que.pop();
  39. for(edge *p=adj[f];p;p=p->nxt)
  40. if( d[f] + 1 < d[p->to] && p->rev->cap > p->rev->flow ) {
  41. d[p->to] = d[f] + 1;
  42. que.push(p->to);
  43. if( p->to == s ) return true;
  44. }
  45. }
  46. return !(d[s] == n + 5);
  47. }
  48. int aug(int x, int tot) {
  49. if( x == t ) return tot;
  50. int sum = 0;
  51. for(edge *&p=cur[x];p;p=p->nxt) {
  52. if( p->cap > p->flow && d[p->to] + 1 == d[x] ) {
  53. int del = aug(p->to, min(p->cap - p->flow, tot - sum));
  54. p->flow += del, p->rev->flow -= del, sum += del;
  55. if( sum == tot ) break;
  56. }
  57. }
  58. return sum;
  59. }
  60. int max_flow(int _s, int _t, int tot) {
  61. s = _s, t = _t; int flow = 0;
  62. while( tot && relabel() ) {
  63. int del = aug(s, tot);
  64. flow += del, tot -= del;
  65. }
  66. return flow;
  67. }
  68. }G;
  69. int A[MAXN + 5], f[MAXN + 5];
  70. FlowGraph::edge *e[MAXN + 5];
  71. pair<int, int>C[MAXN + 5];
  72. int ans[MAXN + 5], cnt;
  73. void solve() {
  74. int N, s, t; scanf("%d", &N);
  75. s = 0, t = 2*N + 1, G.clear(t);
  76. for(int i=1;i<=N;i++)
  77. scanf("%d", &A[i]);
  78. int mx = 0;
  79. for(int i=1;i<=N;i++) {
  80. f[i] = 1;
  81. for(int j=1;j<i;j++)
  82. if( A[j] < A[i] )
  83. f[i] = max(f[i], f[j] + 1);
  84. mx = max(mx, f[i]);
  85. }
  86. for(int i=1;i<=N;i++) {
  87. int x; scanf("%d", &x);
  88. G.addedge(i, i + N, x);
  89. e[i] = G.adj[i];
  90. }
  91. for(int i=1;i<=N;i++) {
  92. if( f[i] == 1 ) G.addedge(s, i, INF);
  93. if( f[i] == mx ) G.addedge(i + N, t, INF);
  94. for(int j=1;j<i;j++)
  95. if( f[j] + 1 == f[i] )
  96. G.addedge(j + N, i, INF);
  97. }
  98. for(int i=1;i<=N;i++)
  99. scanf("%d", &C[i].first), C[i].second = i;
  100. sort(C + 1, C + N + 1);
  101. printf("%d", G.max_flow(s, t, INF));
  102. cnt = 0;
  103. for(int i=1;i<=N;i++) {
  104. int x = C[i].second;
  105. G.s = e[x]->rev->to, G.t = e[x]->to;
  106. if( !G.relabel() ) {
  107. G.max_flow(t, e[x]->to, e[x]->flow), G.max_flow(e[x]->rev->to, s, e[x]->flow);
  108. e[x]->cap = e[x]->flow = e[x]->rev->cap = e[x]->rev->flow = 0;
  109. ans[++cnt] = x;
  110. }
  111. }
  112. sort(ans + 1, ans + cnt + 1);
  113. printf(" %d\n", cnt);
  114. for(int i=1;i<=cnt;i++)
  115. printf("%d%c", ans[i], i == cnt ? '\n' : ' ');
  116. }
  117. int main() {
  118. int T; scanf("%d", &T);
  119. while( T-- ) solve();
  120. }

@details@

woc 为什么我用 long long 就会多 TLE 一个点啊。。。

难道这个数据范围(指 Bi <= 10^9)不应该开 long long 吗。。。

为什么 int 能过啊。。。

@loj - 2196@「SDOI2014」Lis的更多相关文章

  1. LOJ #2196「SDOI2014」LIS

    直接退流复杂度好优越啊 LOJ #2196 题意 一段数列,每个点有点权$ A_i$,删除代价$ B_i$,附加属性$ C_i$ 求最小代价使得$ LIS$长度发生变化,且输出一种$ C_i$字典序最 ...

  2. 「SDOI2014」Lis 解题报告

    「SDOI2014」Lis 题目描述 给定序列 \(A\),序列中的每一项 \(A_i\) 有删除代价 \(B_i\) 和附加属性 \(C_i\). 请删除若干项,使得 \(A\) 的最长上升子序列长 ...

  3. 「SDOI2014」向量集 解题报告

    「SDOI2014」向量集 维护一个向量集合,在线支持以下操作: A x y :加入向量 \((x, y)\): Q x y l r:询问第 \(L\) 个到第 \(R\) 个加入的向量与向量 \(( ...

  4. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  5. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  6. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  7. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  8. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  9. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

随机推荐

  1. 链接的属性href=“?” ?该些什么及优缺点

    <a onclick="{jscode}">是很常见的一种js运用方式,使用时经常会加一个href=“###”即<a onclick="{jscode} ...

  2. mysql8.0 的坑 hibernate连接配置坑

    https://blog.csdn.net/qq_36448800/article/details/81180881 这篇文章对于连接配置说的是对的,也比较全面

  3. IO流2 --- File类的常用方法1 --- 技术搬运工(尚硅谷)

    File类的获取功能 @Test public void test2(){ File file1 = new File("hello.txt"); File file2 = new ...

  4. Linux巨好用的

    受不了了windows下配置烦死人.linux一站式搞定,无敌 安装python3 机子上py2 py3共存没问题 安装virtualenv (py3)分割实验环境非常稳 安装mysql 先安装了一 ...

  5. storm 为什么要存在不透明分区事务

    不透明分区事务不区分发新消息还是旧消息,全部用emitPartitionBatch搞定,虽然 emitPartitionBatch返回的X应该是下一批次供自己使用(emitPartitionBatch ...

  6. Leetcode643.Maximum Average Subarray I子数组的最大平均数1

    给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数. 示例 1: 输入: [1,12,-5,-6,50,3], k = 4 输出: 12.75 解释: 最大平均数 (12- ...

  7. linux系统 (实验二)实验楼的课程笔记

    who am i  打印用户名和终端 终端的启动时间 whoami    打印用户名 who 命令其它常用参数 参数 说明 -a 打印能打印的全部 -d 打印死掉的进程 -m 同am i,mom li ...

  8. Spring4.x 基础知识点

    # Spring4.x 基础知识点## 第二章 快速入门- 一般情况下,需要在业务模块包下进一步按分层模块划分子包,如user\dao.user\service.viewspace\dao.views ...

  9. Linux之rpm包管理-yum在线管理

    1.IP地址配置 1.以root登录Linux系统,在终端输入setup启动图形界面menuconfing,如下图所示: 2.选择network configuration ,进入网络配置界面,进入后 ...

  10. IDEA(JAVA)使用json

    首先介绍一下json SON是一种取代XML的数据结构,和xml相比,它更小巧但描述能力却不差,由于它的小巧所以网络传输数据将减少更多流量从而加快速度. JSON就是一串字符串 只不过元素会使用特定的 ...