「九省联考 2018」劈配 解题报告

「九省联考 2018」劈配

写了一个有点奇怪的做法(感觉

首先发现有个比较正常的暴力,就是每次二分重新建图跑,似乎有80分,应该也比较好写,考场应该会写这个。

考虑如果得到了前\(i\)个人的答案(问题1),那么这些人只能在一部分的导师里面反悔,我们把这些边建出来。

然后对于\(i+1\sim n\)个人,每次把它所有边加上,然后看看它在这个排名可不可以达到它的期望,然后再把边撤回。

这里用一个单路增广就好,比较好撤回,可以发现这个单路增广是\(O(n+m)\)

然后每个人跑了\(n\)次单路增广,\(m\)在建一部分边后大小应该是\(nc\)条的,所以总复杂度是\(O(Tn^3C)\),跑不满就可以过


Code:

#include 
#include 
#include 
#include 
const int N=420;
const int M=4e5;
const int inf=0x3f3f3f3f;
template 
void read(T &x)
{
    x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int head[N],thead[N],edge[M],to[M],Next[M],cnt;
void add(int u,int v,int w)
{
    to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
    to[++cnt]=u,edge[cnt]=0,Next[cnt]=head[v],head[v]=cnt;
}
int n,m,s,t,bee[N][N],ans0[N],ans1[N],yuy[N];
int pre[N],vis[N],ti,Id,flag;
void dfs(int now)
{
    vis[now]=ti;
    if(now==t)
    {
        while(pre[now])
        {
            if((now>n&&now<=n+m)||now==t+1) Id=now;
            now=to[pre[now]^1];
        }
        flag=0;
        return;
    }
    for(int v,i=head[now];flag&&i;i=Next[i])
        if(edge[i])
        {
            if(vis[v=to[i]]==ti) continue;
            pre[v]=i;
            dfs(v);
        }
}
struct koito_yuu
{
    int v,x;//第几志愿,节点标号
    koito_yuu(){}
    koito_yuu(int V,int X){v=V,x=X;}
    bool friend operator <(koito_yuu a,koito_yuu b){return a.v>b.v;}
}yuu[N][N];
void work()
{
    read(n),read(m);
    s=n+m+1,t=s+1;
    for(int b,i=1;i<=m;i++) read(b),add(i+n,t,b);
    add(t+1,t,inf);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)//第i个人对第j个导师为第几志愿
            read(bee[i][j]),yuu[i][j]=koito_yuu(bee[i][j],j+n);
        bee[i][m+1]=m+1,yuu[i][m+1]=koito_yuu(m+1,t+1);
        std::sort(yuu[i]+1,yuu[i]+1+m+1);
        /*a[j]=mp(bee[i][j],j);
        std::sort(a+1,a+1+m);
        a[m+1]=mp(m+1,t+1);
        bee[i][m+1]=m+1;
        for(int j=m;~j;j--)
            if(a[j].fisrt!=a[j+1].fisrt)
            {
                endro[i][a[j+1].fisrt]=head[i];//第i个人第j个志愿的
                if(a[j].fisrt) add(i,a[j].second,1);
            }
        */
    }
    for(int i=1;i<=n;i++) read(yuy[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            int tmp=cnt;
            for(int k=1;k<=t+1;k++) thead[k]=head[k];
            add(s,j,1);
            for(int k=1;k<=m+1;k++)
            {
                if(!yuu[j][k].v) break;
                add(j,yuu[j][k].x,1);
            }
            flag=1,++ti;
            dfs(s);
            Id=bee[j][Id==t+1?m+1:Id-n];
            if(j==i) ans0[i]=Id;
            if(Id<=yuy[j]) ans1[j]=i;
            cnt=tmp;
            for(int k=1;k<=t+1;k++) head[k]=thead[k];
        }
        add(s,i,1);
        for(int j=1;j<=m+1;j++)
            if(ans0[i]==yuu[i][j].v)
                add(i,yuu[i][j].x,1);
        flag=1,++ti;
        dfs(s);
        int now=t;
        while(pre[now])
        {
            --edge[pre[now]];
            ++edge[pre[now]^1];
            now=to[pre[now]^1];
        }
    }
    for(int i=1;i<=n;i++) printf("%d ",ans0[i]);
    puts("");
    for(int i=1;i<=n;i++) printf("%d ",i-ans1[i]);
    puts("");
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int T,C;
    read(T),read(C);
    while(T--)
    {
        //待会清空放在这里,要检查
        memset(head,0,sizeof head);
        memset(pre,0,sizeof pre);
        memset(ans1,0,sizeof ans1);
        cnt=1;
        work();
    }
    return 0;
}

2019.3.19

转载于:https://www.cnblogs.com/butterflydew/p/10556380.html

你可能感兴趣的:(「九省联考 2018」劈配 解题报告)