• [CTSC2017]最长上升自序列(伪题解)(Dilworth's theorem+网络流)


    部分分做法很多,但每想出来一个也就多5~10分。正解还不会,下面是各种部分分做法:

    Subtask 1:k=1

    LCS长度最长为1,也就是说不存在j>i和a[j]>a[i]同时成立。显然就是一个LDS,树状数组直接求即可。

    Subtask 2:k=2

    最多两个,也就是可以由两个LCS拼起来,f[i][j]表示第一个LCS以i结尾,第二个以j结尾的方案数,转移显然。

    Subtask 3:k=2

    树状数组优化DP,复杂度由$O(n^3)$降为$O(n^2 log n)$

    Subtask 4,5:B<=8

    DP套DP:https://www.cnblogs.com/clnchanpin/p/7357564.html

    一般与“子序列”同时出现,如最长上升自序列,最长公共自序列等。

    Subtask 6,7:

    一个显然的定理:一个序列的LCS最大为k意味着这个序列最少可以由k个不相交的LDS组成。

    考虑网络流,下面(a,b)表示容量为a,费用为b的边。

    拆点,in[x]向out[x]连(1,-1)的边,每次和下一个小于这个数的位置连(1,0)的边,增设源汇,最多增广k次即可。

    $O(Kn^3)$

    Subtask 8,9:

    上面的方法点数为$n$,边数为$n^2$,启发我们用线段树优化建图。

    这里用树状数组就行了。

    点数$nlog n$,边数$nlog n$。

    $O(K(nlog n)^2)$

    Subtask 10,11,12:

    Johnson多源最短路算法:

    传统的Floyd是$O(n^3)$的,已经很优秀了。但是如果我们对每个点跑一次Dijkstra,可以得到$O(n^2log m)$这个更好的复杂度。

    但是Dijkstra不能跑有负权边的情况。

    考虑增设超级源S并向每个点连长度为0的边,然后跑单源最短路,接着将每条边(u,v,w)改成(u,v,w+(dis[u]-dis[v])),其中dis[u]表示S到u的最短路。

    这样跑Dijkstra就是正确的了,转移的时候记录pre方便最后求出最短路长度。

    不了解如何运用到这道题上。

    优化:可以直接用数组代替堆降低复杂度。

    Subtask 13~20:

    完全不理解的杨氏矩阵理论。

    不断分析将复杂度依次降为:$O(n^2log n+Qlog n)$,$O(nsqrt{n}log n)$,$O(nsqrt{nlog n})$。

    附:树状数组+网络流代码(15pts)

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define rep(i,l,r) for (int i=(l); i<=(r); i++)
    using namespace std;
    
    const int N=100010;
    int x,y,fir[N],pre[N],a[N],b[N],inq[N],dis[N],n,m,cnt=1;
    int in[N],out[N],ans[N],BIT[N],S,T,cost,lim,obt,tot;
    
    struct edge{
        int to,nxt,f,c;
        edge () {}
        edge (int x,int y,int z,int l){ to=y; nxt=fir[x]; f=z; c=l; fir[x]=cnt; }
    }e[6*N];
    
    void add(int x,int y,int z,int l){ e[++cnt]=edge(x,y,z,l); e[++cnt]=edge(y,x,0,-l); }
    
    bool spfa(){
        int i,x; queue<int> q;
        for(i=1;i<=T;i++) dis[i]=1;
        dis[S]=0; q.push(S);
        while(!q.empty()){
            x=q.front(); q.pop();
            for(i=fir[x];i;i=e[i].nxt)
                if(e[i].f&&dis[e[i].to]>dis[x]+e[i].c){
                    dis[e[i].to]=dis[x]+e[i].c; pre[e[i].to]=i;
                    if(!inq[e[i].to]) q.push(e[i].to),inq[e[i].to]=1;
                }
            inq[x]=0;
        }
        return dis[T]!=1;
    }
    
    int aug(){
        int x,flow=1e9;
        for(x=T;x!=S;x=e[pre[x]^1].to) flow=min(flow,e[pre[x]].f);
        for(x=T;x!=S;x=e[pre[x]^1].to)
            cost+=e[pre[x]].c*flow,e[pre[x]].f-=flow,e[pre[x]^1].f+=flow;
        return flow;
    }
    
    int dinic(){ int res=0; while(spfa()) res+=aug(); return res; }
    
    void modify(int p,int x){
        for(;x<=obt;x+=x&-x){
            tot++;
            if(BIT[x]) add(tot,BIT[x],n,0);
            add(tot,p,1,0); BIT[x]=tot;
        }
    }
    
    void ask(int p,int x){ for (;x;x-=x&-x) if (BIT[x]) add(p,BIT[x],1,0); }
    
    int main(){
        freopen("lis.in","r",stdin);
        freopen("lis.out","w",stdout);
        scanf("%d%d",&n,&m);
        if (n>100) { rep(i,1,m) printf("0
    "); return 0; }
        rep(i,1,n) scanf("%d",&a[i]),b[i]=a[i];
        sort(b+1,b+n+1); obt=unique(b+1,b+n+1)-b-1;
        rep(i,1,n) a[i]=lower_bound(b+1,b+obt+1,a[i])-b;
        rep(i,1,n) in[i]=i,out[i]=i+n,add(in[i],out[i],1,-1);
        tot=2*n;
        for (int i=n;i;i--) ask(out[i],a[i]),modify(in[i],a[i]);
        S=tot+1; T=S+1;
        rep(i,1,n) add(S,in[i],1,0),add(out[i],T,1,0);
        cost=lim=0;
        while(spfa()) aug(),ans[++lim]=-cost;
        while(m--) scanf("%d%d",&x,&y),printf("%d
    ",ans[min(y,lim)]);
        return 0;
    }
  • 相关阅读:
    1015,存储过程,视图
    1009,数据库查询,聚合函数,日期时间函数
    1008,数据库表格创建,名称,格式

    公历和农历转换的JS代码
    面向对象之封装
    HTML之锚点
    HTML之css+div
    HTML基础
    SQL之定义变量
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8886115.html
Copyright © 2020-2023  润新知