洛谷 3701「伪模板」主席树(最大流)

题目背景

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

题目描述

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

 

研究发现,这五种人的输赢如上图所示(一样的人不能PK),箭头指向输的人。至于为什么,留给同学们自己思考。

比赛如期进行。

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

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

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

那么问题来了

现在给定N,M(1≤N≤100,1≤M≤1000),A和B每一个人所属种类(J,HK,W,YYY或E)以及每一个人的生命,生命不超过50.请你算算A最多能够赢得多少场比赛呢。

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

输入输出格式

输入格式:
第一行包含两个数N,M,含义看上面。

第二行N个字串(J,HK,W,YYY或E),表示byx的人所属种类,用空格隔开。

第三行N个字串(J,HK,W,YYY或E),表示手气君的人所属种类,用空格隔开。

第四行N个数,表示byx的人的生命。

第五行N个数,表示手气君的人的生命。

输出格式:
一个数,byx能赢的场次

输入输出样例

输入样例#1: 复制
3 3
J W YYY
J HK E
2 2 2
2 2 2
输出样例#1: 复制
3
说明

第一场主席赢记者,第二场高人赢女王,第三场膜法师赢记者

题解:源点向每个人建容量为hp的边,如果是长者,则容量增加魔法师条.每个人向另一组中克制的人建容量为一的边,另一组每个人向汇点建容量为hp的边,然后跑一遍dinic就搞定了

代码如下:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;

int head[100010],next[100010],w[100010],v[100010],deep[100010],cur[100010];
int s,t,cnt;

void init()
{
    cnt=-1;
    memset(head,-1,sizeof(head));
    memset(next,-1,sizeof(next));
}

void add(int from,int to,int cost)
{
    cnt++;
    next[cnt]=head[from];
    w[cnt]=cost;
    v[cnt]=to;
    head[from]=cnt;
}

void add_edge(int from,int to,int cost)
{
    add(from,to,cost);
    add(to,from,0);
}

int bfs(int s,int t)
{
    queue<int> q;
    memset(deep,0,sizeof(deep));
    deep[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u]; i!=-1; i=next[i])
        {
            if(!deep[v[i]]&&w[i])
            {
                deep[v[i]]=deep[u]+1;
                q.push(v[i]);
            }
        }
    }
    if(!deep[t])
    {
        return 0;
    }
    return 1;
}

int dfs(int u,int t,int dist)
{
    if(u==t)
    {
        return dist;
    }
    for(int i=head[u]; i!=-1; i=next[i])
    {
        if(w[i]&&deep[v[i]]==deep[u]+1)
        {
            int di=dfs(v[i],t,min(dist,w[i]));
            if(di>0)
            {
                w[i]-=di;
                w[i^1]+=di;
                return di;
            }
        }
    }
    return 0;
}

int dinic(int s,int t)
{
    int ans=0;
    while(bfs(s,t))
    {
        while(int d=dfs(s,t,inf))
        {
            ans+=d;
        }
    }
    return ans;
}

int main()
{
    int n,m,kd1[110],kd2[110],hp1[110],hp2[110],cnt1=0,cnt2=0;
    char s1[20];
    init();
    scanf("%d%d",&n,&m);
    s=0;t=2*n+1;
    for(int i=1;i<=n;i++)
    {
        cin>>s1;
        if(s1[0]==‘W‘)
        {
            kd1[i]=1;
        }
        if(s1[0]==‘J‘)
        {
            kd1[i]=2;
        }
        if(s1[0]==‘E‘)
        {
            kd1[i]=3;
        }
        if(s1[0]==‘Y‘)
        {
            kd1[i]=4;
            cnt1++;
        }
        if(s1[0]==‘H‘)
        {
            kd1[i]=5;
        }
    }
    for(int i=1;i<=n;i++)
    {
        cin>>s1;
        if(s1[0]==‘W‘)
        {
            kd2[i]=1;
        }
        if(s1[0]==‘J‘)
        {
            kd2[i]=2;
        }
        if(s1[0]==‘E‘)
        {
            kd2[i]=3;
        }
        if(s1[0]==‘Y‘)
        {
            kd2[i]=4;
            cnt2++;
        }
        if(s1[0]==‘H‘)
        {
            kd2[i]=5;
        }
    }
    for(int i=1;i<=n;i++)
    {
        cin>>hp1[i];
    }
    for(int i=1;i<=n;i++)
    {
        cin>>hp2[i];
    }
    for(int i=1;i<=n;i++)
    {
        add_edge(s,i,hp1[i]);
        add_edge(i+n,t,hp2[i]);
        if(kd1[i]==2)
        {
            add_edge(s,i,cnt1);
        }
        if(kd2[i]==2)
        {
            add_edge(i+n,t,cnt2);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(kd1[i]==1)
            {
                if(kd2[j]==3||kd2[j]==4)
                {
                    add_edge(i,j+n,1);
                }
            }
            if(kd1[i]==2)
            {
                if(kd2[j]==1||kd2[j]==5)
                {
                    add_edge(i,j+n,1);
                }
            }
            if(kd1[i]==3)
            {
                if(kd2[j]==2||kd2[j]==4)
                {
                    add_edge(i,j+n,1);
                }
            }
            if(kd1[i]==4)
            {
                if(kd2[j]==2||kd2[j]==5)
                {
                    add_edge(i,j+n,1);
                }
            }
            if(kd1[i]==5)
            {
                if(kd2[j]==1||kd2[j]==3)
                {
                    add_edge(i,j+n,1);
                }
            }
        }
    }
    int ans=min(dinic(s,t),m);
    printf("%d\n",ans);
}

评级竟然是NOI/NOI+/CTSC?emmm....

原文地址:https://www.cnblogs.com/stxy-ferryman/p/8584542.html

时间: 2024-07-28 21:23:24

洛谷 3701「伪模板」主席树(最大流)的相关文章

[Luogu 3701] 「伪模板」主席树

[Luogu 3701] 「伪模板」主席树 <题目链接> 这是一道网络流,不是主席树,不是什么数据结构,而是网络流. 题目背景及描述都非常的暴力,以至于 Capella 在做此题的过程中不禁感到生命流逝. S 向 byx 的树中的每一个人连有向边,手气君的树中的每一个人向 T 连有向边,边权为这个人的寿命.统计同一棵树中的膜法师数量 x.如果一个人是主席,那么边权要加上 x.(续得好啊) 然后,如果 byx 树中的一个点 i 能赢手气君树中的点 j,那么连 i->j,边权为 1. 跑最大

P3701 「伪模板」主席树

P3701 「伪模板」主席树 题目背景 byx和手气君都非常都非常喜欢种树.有一天,他们得到了两颗奇怪的树种,于是各自取了一颗回家种树,并约定几年后比一比谁种出来的树更加牛x. 题目描述 很快,这棵树就开花结果了.byx和手气君惊讶的发现,这是一棵主席树,树上长满了主席和主席的朋友们.这棵树上一共有五种人,主席(J),记者(HK),高人(W),女王(E)和膜法师(YYY).他们发现,他们的主席树上的人数相同,都为N. 研究发现,这五种人的输赢如上图所示(一样的人不能PK),箭头指向输的人.至于为

【原创】洛谷 LUOGU P3373 【模板】线段树2

P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字的个数.操作的总个数和模数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k 操作2: 格式:2 x

【洛谷P3372】【模板】线段树 1

题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式: 输出包含若干行整

「模板」 线段树——区间乘 &amp;&amp; 区间加 &amp;&amp; 区间求和

「模板」 线段树--区间乘 && 区间加 && 区间求和 <题目链接> 原来的代码太恶心了,重贴一遍. #include <cstdio> int n,m; long long p; class SegmentTree { private: struct Node { int l,r; long long v,mul,add; Node *c[2]; Node(int l,int r):l(l),r(r),mul(1LL),add(0LL) { c[

【Luogu】P3384主席树模板(主席树查询K小数)

YEAH!我也是一个AC主席树模板的人了! 其实是个半吊子 我将尽量详细的讲出我的想法. 主席树太难,我们先搞普通线段树好了 普通线段树怎么做?我的想法是查询K次最小值,每次查完把查的数改成INF,查完再改回来... MDZZ 于是就有了主席树. 先不考虑主席树,我们来考虑一个奇特的线段树. 一般的线段树,数列位置是下标,而把数列维护值作为线段树中存的元素. 那我们如果反过来,把数列元素当做线段树的下标...??? 比如说数列[4 2 3 1] 如果线段树的下标是1.2.3.4......? 那

洛谷P3434 [POI2006]KRA-The Disks(线段树)

洛谷题目传送门 \(O(n)\)的正解算法对我这个小蒟蒻真的还有点思维难度.洛谷题解里都讲得很好. 考试的时候一看到300000就直接去想各种带log的做法了,反正不怕T...... 我永远只会有最直观的思路(最差的程序效率) 题目相当于每次让我们找区间\([1,las-1]\)中上数第一个比当前盘子半径小的位置(las为上一次盘子掉到的位置)于是用线段树无脑搞一下,维护区间最小值,每次找这个位置,能往左跳就往左,不能的话再往右,当然如果超过了las-1就不用找了,直接放在las上面(相当于la

「不会」矩阵树定理

定理不会证,也不会用 「小z的房间」 暴力建图 「重建」 矩阵树求的是$\sum\limits_{T} \prod\limits_{e\in T} w(e)$ 而要求的是$\sum\limits_{T} \prod\limits_{e\in T}p(e)*\prod\limits_{e isnot\in T} 1-p(e)$ 即$\prod\limits_e 1-p(e) \sum\limits_{T} \prod\limits_{e\in T}p(e)/(1-p(e))$ 设$w(e)=p(e

【模板】主席树

主席树..高大上的名字..原名叫可持久化线段树..也有人叫函数式线段树(其实叫什么都不重要). 本来的作用就是字面意思..持久化的线段树,支持修改之后查找某次修改之前的版本.(在NOIP之前在算法导论上看到过,当时觉得没什么,现在才知道好厉害的数据结构) 具体来怎么实现呢..其实就是每次修改的时候都新开一个根节点然后把和修改有关的区间一路新建下去,与修改无关的区间就继承上次修改版本的节点,这样会节省空间. 来应用一下,一个基础的应用就是区间K小值查询(poj2104).即给定一个序列,给出L,R