3532: [Sdoi2014]Lis

57 篇文章 0 订阅

3532: [Sdoi2014]Lis

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 838   Solved: 303
[ Submit][ Status][ Discuss]

Description

 给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci。请删除若
干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案。
    如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种。
  

Input

  输入包含多组数据。
    输入的第一行包含整数T,表示数据组数。接下来4*T行描述每组数据。
    每组数据的第一行包含一个整数N,表示A的项数,接下来三行,每行N个整数A1..An,B1.,Bn,C1..Cn,满足1 < =Ai,Bi,Ci < =10^9,且Ci两两不同。

Output

    对每组数据,输出两行。第一行包含两个整数S,M,依次表示删去项的代价和与数量;接下来一行M个整数,表示删去项在4中的的位置,按升序输出。

Sample Input

1
6
3 4 4 2 2 3
2 1 1 1 1 2
6 5 4 3 2 1

Sample Output

4 3
2 3 6
解释:删去(A2,43,A6),(A1,A6),(A2,43,44,A5)等都是合法的方案,但
{A2,43,A6)对应的C值的字典序最小。

HINT

1 < =N < =700     T < =5

Source

[ Submit][ Status][ Discuss]

假如没有第二个子问题,那显然是个最小割裸题了
dp出最长上升子序列,然后对于这些可行子序列建边,跑出的最大流就是答案
对于第二问,先考虑对于原图中的一条边,是否可以存在于最小割
残量网络中,一条边可以存在于最小割集,当且仅无法找到任何一条从该边起点到终点的路径
那么就可以贪心地每次选择C最小的边,检查是否可以存在于最小割集,然后删除它的影响
删除影响可以通过删去边,重新跑一遍最大流解决

比较好一点的,是记该边起点为Ai,终点为Bi,跑一遍(Ai,s)最大流,跑一遍(Ai,t)最大流,再删去这条边

听说这是一种叫退流算法的东西。。。?
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
//#include<ctime>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 1505;
const int maxm = 4E5 + 40;
typedef int LL;
const int INF = ~0U>>1;
 
struct E{
    int to; int cap,flow; E(){}
    E(int to,LL cap,LL flow): to(to),cap(cap),flow(flow){}
}edgs[maxm];
 
struct data{
    int Num,key; data(){}
    data(int Num,int key): Num(Num),key(key){}
    bool operator < (const data &B) const {return key < B.key;}
}D[maxn];
 
int n,m,tot,cnt,Cnt,T,s,t,tp,Ans[maxn],A[maxn],B[maxn],cur[maxn]
    ,L[maxn],f[maxn],a[maxn],b[maxn],id[maxn],vis[maxn];
 
vector <int> v[maxn];
queue <int> Q;
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
char ch[20];
void Print(LL x)
{
    if (!x) {putchar('0'); return;} int len = 0;
    while (x) ch[++len] = x % 10LL,x /= 10LL;
    for (int i = len; i; i--) putchar(ch[i] + '0');
}
 
bool BFS(int ss,int tt)
{
    for (int i = 0; i <= tot; i++) L[i] = 0;
    Q.push(ss); L[ss] = 1;
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop();
        for (int i = 0; i < v[k].size(); i++)
        {
            E e = edgs[v[k][i]];
            if (e.cap == e.flow || L[e.to]) continue;
            L[e.to] = L[k] + 1; Q.push(e.to);
        }
    }
    return L[tt];
}
 
LL Dinic(int x,LL res,int tt)
{
    if (x == tt) return res; LL flow = 0;
    for (int &i = cur[x]; i < v[x].size(); i++)
    {
        E &e = edgs[v[x][i]];
        if (e.cap == e.flow || L[e.to] != L[x] + 1) continue;
        LL f = Dinic(e.to,min(res,e.cap - e.flow),tt);
        if (!f) continue; flow += f; e.flow += f;
        edgs[v[x][i]^1].flow -= f; res -= f;
        if (!res) return flow;
    }
    if (!flow) L[x] = -1; return flow;
}
 
LL MaxFlow(int ss,int tt)
{
    LL ret = 0;
    while (BFS(ss,tt))
    {
        for (int i = 0; i <= tot; i++) cur[i] = 0;
        ret += Dinic(ss,INF,tt);
    }
    return ret;
}
 
bool Check(int x)
{
    vis[A[x]] = ++Cnt; Q.push(A[x]);
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop();
        for (int i = 0; i < v[k].size(); i++)
        {
            E e = edgs[v[k][i]];
            if (e.cap == e.flow || vis[e.to] == Cnt) continue;
            vis[e.to] = Cnt; Q.push(e.to);
        }
    }
    return vis[B[x]] != Cnt;
}
 
void Work(int x)
{
    MaxFlow(t,B[x]); MaxFlow(A[x],s);
    edgs[id[x]].cap = edgs[id[x]].flow = 0;
    edgs[id[x]^1].cap = edgs[id[x]^1].flow = 0;
}
 
void Add(int x,int y,LL cap)
{
    v[x].push_back(cnt); edgs[cnt++] = E(y,cap,0);
    v[y].push_back(cnt); edgs[cnt++] = E(x,0,0);
}
 
void Clear()
{
    for (int i = 0; i <= tot; i++) v[i].clear();
    memset(f,0,sizeof(f)); tot = tp = cnt = 0;
}
 
void Solve()
{
    n = getint(); int Max = 0;
    for (int i = 1; i <= n; i++) A[i] = ++tot,B[i] = ++tot; t = ++tot;
    for (int i = 1; i <= n; i++) a[i] = getint();
    for (int i = 1; i <= n; i++) b[i] = getint();
    for (int i = 1; i <= n; i++) D[i] = data(i,getint());
    sort(D + 1,D + n + 1);
    for (int i = 2; i <= n; i++)
    {
        for (int j = 1; j < i; j++)
            if (a[i] > a[j]) f[i] = max(f[i],f[j] + 1);
        Max = max(Max,f[i]);
    }
    for (int i = 1; i <= n; i++)
        if (f[i] == Max) Add(B[i],t,INF);
    for (int i = 1; i <= n; i++)
    {
        if (!f[i]) Add(s,A[i],INF);
        for (int j = i + 1; j <= n; j++)
            if (a[i] < a[j] && f[i] + 1 == f[j])
                Add(B[i],A[j],INF);
        Add(A[i],B[i],b[i]); id[i] = cnt - 1;
    }
    LL sum = MaxFlow(s,t);
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    for (int i = 1; i <= n; i++)
        if (Check(D[i].Num))
        {
            Work(D[i].Num); Ans[++tp] = D[i].Num;
            //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
        }
    sort(Ans + 1,Ans + tp + 1);
    Print(sum); putchar(' '); Print(tp); puts("");
    for (int i = 1; i < tp; i++) Print(Ans[i]),putchar(' ');
    Print(Ans[tp]); puts(""); Clear();
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    T = getint(); while (T--) Solve();
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值