伪模板————主席树
作者:互联网
P3701: https://www.luogu.com.cn/problem/P3701
这道题是网络流一道经典的题目
不过有两个不科学的坑点
既然是网络流,那么关键点自然是在建图上
我们把我们的点叫a,对方的点叫b,源点是s,汇点是t
我们思考,由于life的限制,所以我们体现在图上就是:
对每一个a从s连一条长life的边
对每一个b连一条长life的边到t
这样我们就能保证life的限制
接下来,考虑,由于我们要求的是赢的最多的
所以我们只要考虑我们能赢的关系就可以了
如果a能赢b那么从a连一条长为1的边,保证两两只会打一场比赛
接下来就要考虑续命的问题了
本来根据蛤一定律,即时间守恒定律:时间不会凭空产生或消失,只会从一个人转移到另一个人。
所以我们可以考虑,我们让膜法师续给主席1s,即把膜法师的1*life续给主席,表现在图上就是把膜法师的一个流量分给主席
即对于每个膜法师,向每一个主席连一条长为1的边,表示每个膜法师能分给每个主席1流量
然而,这道题不遵循时间守恒定律,即膜法师可以不用减少自己的时间给主席续命,这是第一个坑点
就要思考,如何在不消耗自己时间的情况下续命:当然是让别人给主席续命了
所以我们可以建立一些虚点,数量与己方膜法师数量相等,从s连一条inf的边(反正续不死,爱续多少续多少),然后向己方每一位主席建一条长度为1的边(每位膜法师只能给每位主席续1s)
至于对方,
考虑到我们只考虑了我们能赢的关系,在对方那么,对我们来说我们不会和它能赢的打,所以对对手来说,其实他们死的越快输的越少
所以,对于对手,给主席续命一定是非最优决策,所以可以不考虑
这样建完后,dinic,交一发——>90
嗯?嗯??嗯???
没错啊,为什么只有90,难道对手也会续命,一改,果然
所以我们也要类似自己的方法一样建虚点,连边,这是第二个坑点
然后跑dinic就可以了
#include<bits/stdc++.h> #define il inline using namespace std; const int N = 2e3+4; int n,m,s,t; struct edge { int next,to,w; }p[200*N]; int head[N],num; il void ad(int x,int y,int z){p[++num]=edge{head[x],y,z};head[x]=num;} il void add(int x,int y,int z) { ad(x,y,z); ad(y,x,0); // printf("%d %d %d\n",x,y,z); } il int qfj(int x){return ((x-1)^1)+1;} int dep[N],cur[N]; bool bfs(int s,int t) { for(int i=s;i<=t;i++) cur[i]=head[i]; queue<int> q; q.push(s); memset(dep,0,sizeof(dep)); dep[s]=1; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=p[i].next) { int v=p[i].to; if(!dep[v]&&p[i].w) { dep[v]=dep[u]+1; q.push(v); } } } //printf("%d\n",dep[t]); return dep[t]; } int dfs(int u,int t,int limit) { if(u==t||!limit) return limit; int flow=0,w; for(int i=head[u];i;i=p[i].next) { int v=p[i].to; if(dep[v]==dep[u]+1&&(w=dfs(v,t,min(limit,p[i].w)))) { // printf("%d %d %d\n",u,v,w); p[i].w-=w; flow+=w; p[qfj(i)].w+=w; limit-=w; if(!limit) break; } } return flow; } int dinic(int s,int t) { int mxfl=0; while(bfs(s,t)) mxfl+=dfs(s,t,1e9); return mxfl; } int ans; int a[N],lf[N],w1[9],w2[9]; int main() { // freopen("3376.out","w",stdout); cin>>n>>m; int x=0,y=0; for(int i=1;i<=n;i++) { string s; cin>>s; // cout<<s<<endl; if(s[0]=='Y') { a[i]=1; x++; } if(s[0]=='H') a[i]=2; if(s[0]=='W') a[i]=3; if(s[0]=='J') a[i]=4; if(s[0]=='E') a[i]=5; } for(int i=n+1;i<=n+n;i++) { string s; cin>>s; if(s[0]=='Y') { a[i]=1; y++; } if(s[0]=='H') a[i]=2; if(s[0]=='W') a[i]=3; if(s[0]=='J') a[i]=4; if(s[0]=='E') a[i]=5; } for(int i=1;i<=n+n;i++) scanf("%d",lf+i); s=0;t=n+n+1+x+y; w1[1]=2;w2[1]=4; w1[2]=3;w2[2]=5; w1[3]=1;w2[3]=5; w1[4]=2;w2[4]=3; w1[5]=1;w2[5]=4; for(int i=1;i<=n;i++) add(s,i,lf[i]); for(int i=n+1;i<=n+n;i++) add(i,t,lf[i]); for(int i=1;i<=n;i++) for(int j=n+1;j<=n+n;j++) if(a[j]==w1[a[i]]||a[j]==w2[a[i]]) { add(i,j,1); // printf("#%d %d %d %d %d\n",i,j,a[j],w1[i],w2[i]); } // printf("%d %d\n",s,t); for(int i=n+n+1;i<=n+n+x;i++) { add(s,i,1e9); for(int j=1;j<=n;j++) if(a[j]==4) add(i,j,1); } for(int i=n+n+1+x;i<t;i++) { add(i,t,1e9); for(int j=n+1;j<=n+n;j++) if(a[j]==4) add(j,i,1e9); } ans=dinic(s,t); cout<<min(ans,m); return 0; }
这个故事告诉我们:
给主席续命是绝对的!无论何时,无论何地,无论何种情况,无论对我们是否有利,我们都要续,要加大力度地续
标签:int,dep,limit,模板,续命,主席,法师 来源: https://www.cnblogs.com/shenbear/p/12269548.html