P3701 「伪模板」主席树

Posted five20

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3701 「伪模板」主席树相关的知识,希望对你有一定的参考价值。

题目背景

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

说明

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

 

Solution:

  本题最大流。

  读完题后,不难发现是一张二分图的模型,而且角色克制关系也给定了,那么就是个傻逼建图了。

  我们由源点向byx的每个人连边流量为生命值,由手气君的每个人向汇点连边流量为生命值,对于byx的每个节点按照击败关系向手气君的节点连边流量为$1$,由于$YYY$可以为每个$J$加血,所以统计下byx和手气君各自的$YYY$个数,再对于各自的$J$都补建$YYY$个数为流量的边就好了。(开始WA的原因:注意不能直接统计角色总血量然后对每种角色建图,因为两个角色之间只能比一次,而且每个$YYY$可以为每个$J$加一次血,都有流量限制,所以只能$n^2$建图)

代码:

/*Code by 520 -- 9.25*/
#include<bits/stdc++.h>
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
using namespace std;
const int N=100005,inf=233333333;
int n,m,s,t,dis[520],a[6],b[6];
int to[N],net[N],w[N],cnt=1,h[N];
map<string,int>mp;

il void add(int u,int v,int c){
    to[++cnt]=v,net[cnt]=h[u],h[u]=cnt,w[cnt]=c;
    to[++cnt]=u,net[cnt]=h[v],h[v]=cnt,w[cnt]=0;
}

queue<int>q;
bool bfs(){
    memset(dis,-1,sizeof(dis));
    dis[s]=0,q.push(s);
    while(!q.empty()){
        RE int u=q.front();q.pop();
        for(RE int i=h[u];i;i=net[i])
            if(w[i]&&dis[to[i]]==-1) dis[to[i]]=dis[u]+1,q.push(to[i]);
    }
    return dis[t]!=-1;
}

int dfs(int u,int op){
    if(u==t) return op;
    int flow=0,used=0;
    for(RE int i=h[u];i;i=net[i]){
        int v=to[i];
        if(w[i]&&dis[to[i]]==dis[u]+1){
            used=dfs(to[i],min(w[i],op));
            if(!used) continue;
            flow+=used,op-=used;
            w[i]-=used,w[i^1]+=used;
            if(!op) break;
        }
    }
    if(!flow) dis[u]=-1;
    return flow;
}

int main(){
    scanf("%d%d",&n,&m),t=250;
    mp["J"]=1,mp["HK"]=2,mp["W"]=3,mp["YYY"]=4,mp["E"]=5;
    string ca[105],cb[105];int na[105],nb[105];
    int ta=0,tb=0;
    For(i,1,n) cin>>ca[i];
    For(i,1,n) cin>>cb[i];
    For(i,1,n) {
        cin>>na[i],add(s,i,na[i]);
        if(mp[ca[i]]==4) ta++;
    }
    For(i,1,n) {
        cin>>nb[i],add(i+100,t,nb[i]);
        if(mp[cb[i]]==4) tb++;
    }
    For(i,1,n) {
        if(mp[ca[i]]==1) add(s,i,ta);
        if(mp[cb[i]]==1) add(i+100,t,tb);
    }
    For(i,1,n) For(j,1,n){
        int p=mp[ca[i]],q=mp[cb[j]];
        if(p==1&&(q==3||q==2)) add(i,j+100,1);
        if(p==2&&(q==3||q==5)) add(i,j+100,1);
        if(p==3&&(q==4||q==5)) add(i,j+100,1);
        if(p==4&&(q==2||q==1)) add(i,j+100,1);
        if(p==5&&(q==1||q==4)) add(i,j+100,1);
    }
    int ans=0;
    while(bfs()) ans+=dfs(s,inf);
    cout<<min(ans,m);
    return 0;
}

 

以上是关于P3701 「伪模板」主席树的主要内容,如果未能解决你的问题,请参考以下文章

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

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

主席树模板

主席树 模板

Yangk's 静态主席树-模板

主席树模板