【洛谷P3701】 「伪模板」主席树【网络流】

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/82690318

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P3701

byx和手气君都非常都非常喜欢种树。有一天,他们得到了两颗奇怪的树种,于是各自取了一颗回家种树,并约定几年后比一比谁种出来的树更加牛x。

很快,这棵树就开花结果了。byx和手气君惊讶的发现,这是一棵主席树,树上长满了主席和主席的朋友们。这棵树上一共有五种人,主席(J),记者(HK),高人(W),女王(E)和膜法师(YYY)。他们发现,他们的主席树上的人数相同,都为N。
这里写图片描述

研究发现,这五种人的输赢如上图所示(一样的人不能PK),箭头指向输的人。

比赛如期进行。

byx和手气君要进行M场比赛,每一场比赛他们会选出树上的两个人来比较看谁更牛X。

第i个人寿命为Lifei秒,每次比完赛他们就会 1 s 。当他们生命为 0 s 时他们就不能再比赛了。

同时,当J的寿命为 0 时,同一棵树上的YYY可以为他 + 1 s 。每个YYY只能给每个J续一次。

那么问题来了

现在给定 N , M ,byx和手气君每一个人所属种类 ( J , H K , W , Y Y Y , E ) 以及每一个人的生命,生命不超过 50 。请你算算byx最多能够赢得多少场比赛呢。

数据保证每一场一定都有人用。两个人之间只能比一场。


思路:

AC的第一道黑题!!!

这道题跟主席树有神马关系啊(有个人物叫主席就是主席树???)
正解是网络流。
对于每个byx的数上的人物,将它连向 s ,流量为生命。对于每个手气君树上的人物,将它连向 t ,流量为生命。特别的,对于主席,容量要加上该树的膜法师的个数(每个膜法师可以给它加上一条命)
然后,枚举两棵树上的人物,以胜负关系连边(胜连负),容量为 1
然后跑一边 D i n i c 即可。记住最终答案要和 m 取最小值(总场数不能超过 m 场)


代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#define N 510
#define M 10010
#define Inf 1e9
using namespace std;

int n,m,head[N],dep[N],x,s,t,tot=1,ya,yb,sum,ans;
char a[N][10],b[N][10];

struct edge
{
    int next,to,c;
}e[M];

void add(int from,int to,int c)
{
    tot++;
    e[tot].to=to;
    e[tot].c=c;
    e[tot].next=head[from];
    head[from]=tot;
}

bool bfs()  //分层
{
    memset(dep,0x3f,sizeof(dep));
    dep[s]=0;
    queue<int> q;
    q.push(s);
    while (q.size())
    {
        int u=q.front();
        q.pop();
        for (int i=head[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if (dep[v]>dep[u]+1&&e[i].c)  
            {
                dep[v]=dep[u]+1;  //更新
                q.push(v);
            }
        }
    }
    return (dep[t]<0x3f3f3f3f);
}

int dfs(int u,int low) 
{
    int lows=0;
    if (u==t) return low;
    for (int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if (dep[v]==dep[u]+1&&e[i].c)
        {
            lows=dfs(v,min(low,e[i].c));
            if (!lows) continue;
            e[i].c-=lows;
            e[i^1].c+=lows;
            return lows;
        }
    }
    return 0;
}

int dinic()
{
    while (bfs()&&ans<m)
     while (sum=dfs(s,Inf)&&ans<m)  //当ans>=m时就可以不用跑了
      ans+=sum;
    return min(ans,m);  //其实可以直接写成return ans,因为上面保证了m>ans
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    s=n*2+1;
    t=n*2+2;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i];
        if (a[i][0]=='Y') ya++;  //byx的膜法师个数
    } 
    for (int i=1;i<=n;i++)
    {
        cin>>b[i];
        if (b[i][0]=='Y') yb++;  //手气君的膜法师个数
    } 
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if (a[i][0]=='J') add(s,i,x+ya);
         else add(s,i,x);
        add(i,s,0);
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if (b[i][0]=='J') add(i+n,t,x+yb);
         else add(i+n,t,x);
        add(t,i+n,0);
    }
    for (int i=1;i<=n;i++)
     for (int j=1;j<=n;j++)  //枚举人物
      if (a[i][0]!=b[j][0])
      {
        if (a[i][0]=='W'&&b[j][0]=='Y'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='W'&&b[j][0]=='E'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='J'&&b[j][0]=='W'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='J'&&b[j][0]=='H'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='E'&&b[j][0]=='Y'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='E'&&b[j][0]=='J'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='Y'&&b[j][0]=='J'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='Y'&&b[j][0]=='H'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='H'&&b[j][0]=='E'){add(i,j+n,1);add(j+n,i,0);}
        if (a[i][0]=='H'&&b[j][0]=='W'){add(i,j+n,1);add(j+n,i,0);}
      }
    printf("%d\n",dinic());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/82690318
今日推荐