[bzoj4643]卡常大水题 解题报告

我们考虑从小到大枚举A的最大值,那么B的最大值必然是不升的,这样我们才能得到更优的解。我们便可以对B维护一个指针。那么就是考虑插入一条边、删除一条边,求整个图是否是一个强连通分量。
本来是想用什么别的方法求一下。。但是实在是不会做了,所以就只好每次都暴力tarjan试一下。结果竟然就这么a了。。
时间复杂度 O(n4)5108

但如果我们将邻接矩阵用bitset压起来的话就可以做到 O(n432) ,就可以做到比较科学的复杂度了。
麻烦的是如果tarjan的话需要一个类似lowbit的东西来找dfs树的儿子,而bitset是没有这个东西的,所以我们就需要手写一个bitset。。

代码(bitset):

#include<bits/stdc++.h>
using namespace std;
const int N=150+5;
int n;

typedef long long LL;
const int B=64;
struct BS
{
    LL b[3];
    inline BS operator & (BS & u)
    {
        BS ans;
        for(int i=3;i--;)ans.b[i]=b[i]&u.b[i];
        return ans;
    }
    inline void operator |= (BS & u)
    {
        for(int i=3;i--;)b[i]|=u.b[i];
    }
}low[N],vst,not_vst,succ[N];
int in_deg[N],out_deg[N];
inline void modify(BS & b,int x)
{
    b.b[x/B]^=1LL<<x%B;
}
inline int lowbit(const BS & b)
{
    for(int i=0;i<3;++i)
        if(b.b[i])
            return i*B+__builtin_ctzll(b.b[i]);
    return 0;
}
inline void out(BS & b)
{
    for(int i=n;i;--i)printf("%d",b.b[i/B]>>i%B&1);
}
bool dfs(int node)
{
    BS now=vst;
    modify(vst,node),modify(not_vst,node);
    low[node]=succ[node];
    int tmp;

    /*printf("succ[%d]=",node);
    out(succ[node]);
    puts("");*/

    while(tmp=lowbit(succ[node]&not_vst))
    {
        //printf("%d->%d\n",node,tmp);

        if(!dfs(tmp))return 0;
        low[node]|=low[tmp];
    }

    return node==1||lowbit(low[node]&now);
}
inline bool check()
{
    vst=(BS){0};
    for(int i=n;i;--i)not_vst.b[i/B]|=1LL<<i%B;
    return *min_element(in_deg+1,in_deg+n+1)&&*min_element(out_deg+1,out_deg+n+1)&&dfs(1)&&!lowbit(not_vst);
}

struct ES
{
    int u,v,a,b;
}ea[N*N],eb[N*N];
inline bool cmpa(const ES & u,const ES & v)
{
    return u.a<v.a;
}
inline bool cmpb(const ES & u,const ES & v)
{
    return u.b<v.b;
}
inline void add(ES & e,int B)
{
    if(e.b<=B)
    {
        modify(succ[e.u],e.v);
        ++in_deg[e.u],++out_deg[e.v];
        //printf("add %d->%d\n",e.u,e.v);
    }
}
inline void del(ES & e,int A)
{
    if(e.a<=A)
    {
        modify(succ[e.u],e.v);
        --in_deg[e.u],--out_deg[e.v];
    }
}
int main()
{
    freopen("bzoj4643.in","r",stdin);
    //freopen("bzoj4643.out","w",stdout);
    scanf("%d",&n);
    int etot=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            ea[etot]=(ES){i,j};
            scanf("%d",&ea[etot].a);
            etot+=i!=j;
        }
    etot=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            scanf("%d",&ea[etot].b);
            etot+=i!=j;
        }
    memcpy(eb,ea,sizeof(ea));
    sort(ea+1,ea+etot,cmpa),sort(eb+1,eb+etot,cmpb);

    int ans=2e9;
    for(int i=1,j=etot-1;i<etot;++i)
    {
        add(ea[i],eb[j].b);
        while(i<etot&&ea[i].a==ea[i+1].a)add(ea[++i],eb[j].b);
        for(;j&&ea[i].a+eb[j].b>=ans;--j)del(eb[j],ea[i].a);

        //if(j)printf("---check(%d,%d)---\n",ea[i].a,eb[j].b);

        for(;j&&check();--j)
        {
            //puts("Pass");

            ans=ea[i].a+eb[j].b;
            del(eb[j],ea[i].a);
            while(j&&eb[j].b==eb[j-1].b)del(eb[--j],ea[i].a);

            //if(j)printf("---check(%d,%d)---\n",ea[i].a,eb[j-1].b);
        }

        if(!j)break;
    }
    printf("%d\n",ans);
}

代码(暴力):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cstdlib>
char * cp=(char *)malloc(1000000);
inline void in(int &x)
{
    while(*cp<'0'||*cp>'9')++cp;
    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}

const int N=150+5;
int n;
const int E=N*N;
struct ES
{
    int u,v,a,b;
}e[E];
int a[E],b[E];
bool flag[E];
inline bool cmp_a(const int & u,const int & v)
{
    return e[u].a<e[v].a;
}
inline bool cmp_b(const int & u,const int & v)
{
    return e[u].b<e[v].b;
}
int deg_in[N],deg_out[N];
int dfn[N],low[N],dtot;
int vst[N];
int A,B;
#include<vector>
vector<int> succ[N];
bool tarjan(int node)
{
    vst[node]=1;
    low[node]=dfn[node]=dtot++;
    for(int i=succ[node].size();i--;)
    {
        if(!vst[succ[node][i]]&&!tarjan(succ[node][i]))return 0;
        low[node]=min(low[node],low[succ[node][i]]);
    }
    vst[node]=-1;
    return node==1||low[node]!=dfn[node];
}
int main()
{
    freopen("bzoj4643.in","r",stdin);
    freopen("bzoj4643.out","w",stdout);
    fread(cp,1,1000000,stdin);
    in(n);
    int etot=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(i==j)in(e[etot].a);
            else{
                e[etot]=(ES){i,j};
                in(e[etot++].a);
            }
    etot=1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            in(e[etot].b);
            etot+=i!=j;
        }
    for(int i=etot;--i;)a[i]=b[i]=i;
    sort(a+1,a+etot,cmp_a),sort(b+1,b+etot,cmp_b);

    /*printf("a:");
    for(int i=1;i<etot;++i)printf("(%d,%d,%d,%d) ",e[a[i]].u,e[a[i]].v,e[a[i]].a,e[a[i]].b);
    puts("");
    printf("b:");
    for(int i=1;i<etot;++i)printf("(%d,%d,%d,%d) ",e[b[i]].u,e[b[i]].v,e[b[i]].a,e[b[i]].b);
    puts("");*/

    int ans=0x7fffffff;
    for(int i=1,j=etot-1;i<etot;++i)
    {
        //printf("---%d---\n",i);
        for(;j&&e[a[i]].a+e[b[j]].b>=ans;--j)
            if(flag[b[j]])
            {
                //printf("Del(%d,%d)\n",e[b[j]].u,e[b[j]].v);

                for(int i=succ[e[b[j]].u].size();i--;)
                    if(succ[e[b[j]].u][i]==e[b[j]].v)
                    {
                        succ[e[b[j]].u].erase(succ[e[b[j]].u].begin()+i);
                        break;
                    }
                flag[b[j]]=0;
                --deg_in[e[b[j]].v],--deg_out[e[b[j]].u];
            }
        if(!j)break;
        if(e[a[i]].a+e[a[i]].b<ans)
        {
            //printf("Add(%d,%d)\n",e[a[i]].u,e[a[i]].v);

            succ[e[a[i]].u].push_back(e[a[i]].v);
            flag[a[i]]=1;
            ++deg_in[e[a[i]].v],++deg_out[e[a[i]].u];

            memset(vst,0,sizeof(vst));
            dtot=1;
            for(;*min_element(deg_in+1,deg_in+n+1)&&*min_element(deg_out+1,deg_out+n+1)&&tarjan(1)&&*max_element(vst+1,vst+n+1)==-1;)
            {
                ans=e[a[i]].a+e[b[j]].b;

                for(;j&&e[a[i]].a+e[b[j]].b>=ans;--j)
                    if(flag[b[j]])
                    {
                        for(int i=succ[e[b[j]].u].size();i--;)
                            if(succ[e[b[j]].u][i]==e[b[j]].v)
                            {
                                succ[e[b[j]].u].erase(succ[e[b[j]].u].begin()+i);
                                break;
                            }
                        flag[b[j]]=0;
                        --deg_in[e[b[j]].v],--deg_out[e[b[j]].u];
                    }

                memset(vst,0,sizeof(vst));
                dtot=1;
            }
        }
    }
    printf("%d\n",ans);
}

总结:
①如果要求x+y和最小的话,那么随着x增大y必然要减小才会得到更优解。
②感觉又学了一遍tarjan。。发现low这个东西非常奇怪的,它只是用来标识是否有强连通分量的一个东西,其实并不是什么诸如“能到达的dfs序的最小值”这种定义;但是确实有如果low(x) < dfn(x),那么x便和它的父亲在一个强连通分量中。因为比如考虑(x,y)这条边,但是可能还有从y出去的其他边没有被考虑到,而并不是从y出去能到达的最小的dfs序。
③一定要试一下极小的数据。。莫名wa了很久结果发现2*2的0都过不了。。
④一定要考虑清楚循环边界的问题!!

猜你喜欢

转载自blog.csdn.net/ta201314/article/details/52781125