题解 P3701 【「伪模板」主席树】

钱逸凡

2018-09-06 19:58:18

Solution

~~这题感觉完全没有黑题的难度啊,毕竟连我都会做~~ # 思路:~~很明显的~~最大流 [如果不会最大流](https://www.luogu.org/blog/ONE-PIECE/wang-lao-liu-di-zong-jie) 为什么会想到最大流呢? ### 其实题目里到处都在暗示要用最大流 比如说: 两个人之间只能比一场。--->连一条容量为1的边 每次比完赛他们就会-1s。当他们生命为0s时他们就不能再比赛了。-->每条增广路流1,容量减1,边的容量满了就不能流了 最多能够赢得多少场比赛--->最大流是多少 ## 看吧,到处都在暗示用最大流 # 接下来我们讲建图: ## 定义点: byx和手气君每个人都对应一个点 还要一个s(源点),一个t(汇点),一个tt(假装是汇点,后面要加tt指向t的边来限制场数) ## 加边: 1.s流向byx每个人的边,容量为人的寿命(如果是J就加上YYY的数量) 2.手气君每个人流向tt的边,容量为人的寿命(J要加YYY的数量) 3.按克制关系加边,byx的人流向手气君的人(注意:每个被克的人都要加,例:byx的每个J要对应手气君每个HK和每个W),容量为1 4.tt流向t的边,容量为m(最多比m场) ## 更直观的理解: 样例建好图后是这样的: ![](https://cdn.luogu.com.cn/upload/pic/32097.png) ## 然后跑最大流就完了,由于图很稠密,推荐Dinic [如果不会Dinci](https://www.luogu.org/blog/ONE-PIECE/wang-lao-liu-jiang-xie-zhi-dinic) 代码略丑: ``` #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> #include<vector> using namespace std; const int inf=1<<30; int a[101][2];//分别装byx和手气君的人,1表示J,2表示HK,3表示W,4表示YYY,5表示E int b[101][2];//装寿命 inline void REad(int i,int f){ char c=getchar(); string st=""; while(c>'Z'||c<'A')c=getchar(); while(c>='A'&&c<='Z')st=st+c,c=getchar(); if(st=="J")a[i][f]=1; else if(st=="HK")a[i][f]=2; else if(st=="W")a[i][f]=3; else if(st=="YYY")a[i][f]=4; else if(st=="E")a[i][f]=5; }//f==0表示byx,f==1表示手气君 inline int Read(){ int x=0; char c=getchar(); while(c>'9'||c<'0')c=getchar(); while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); return x; } int head[10101],cur[10101],top=1; int n,m,s,t,tt; struct Node{ int v; int val; int next; }node[101010]; inline void addedge(int u,int v,int val){ node[++top].v=v; node[top].val=val; node[top].next=head[u]; head[u]=top; } inline void add(int u,int v,int val){ addedge(u,v,val); addedge(v,u,0); } int dep[10101],inque[10101]; bool spfa(){ for(register int i=1;i<=t;i++){ dep[i]=inf; inque[i]=0; cur[i]=head[i]; } queue<int>q; q.push(s); dep[s]=0; while(!q.empty()){ int u=q.front(); q.pop(); inque[u]=0; for(int i=head[u];i;i=node[i].next){ int d=node[i].v; if(node[i].val&&dep[d]>dep[u]+1){ dep[d]=dep[u]+1; if(inque[d]==0){ q.push(d); inque[d]=1; } } } } return dep[t]!=inf; } int maxflow,vis; int dfs(int u,int flow){ if(u==t){ vis=1; maxflow+=flow; return flow; } int used=0; for(int i=cur[u];i;i=node[i].next){ cur[u]=i; int d=node[i].v; if(dep[d]==dep[u]+1&&node[i].val){ int low=dfs(d,min(node[i].val,flow-used)); if(low!=0)used+=low,node[i].val-=low,node[i^1].val+=low; } if(used==flow)break; } return used; } int Dinic(){ maxflow=0; while(spfa()){ vis=1; while(vis)vis=0,dfs(s,inf); } return maxflow; } vector<int>J,HK,W,YYY,E; void k(int i,vector<int> a){ int sz=a.size(); for(int j=0;j<sz;j++)add(i,n+a[j],1);//两个人之间只能比一场 } void ad(int i){ if(a[i][0]==4||a[i][0]==5)k(i,J); if(a[i][0]==1||a[i][0]==4)k(i,HK); if(a[i][0]==1||a[i][0]==2)k(i,W); if(a[i][0]==3||a[i][0]==5)k(i,YYY); if(a[i][0]==2||a[i][0]==3)k(i,E); }//按照克制关系给i点加边 int main(){ n=Read(),m=Read(); register int i; int cnt1,cnt2;//两人YYY的数量 cnt1=cnt2=0; s=2*n+1,tt=2*n+2,t=2*n+3; add(tt,t,m);//最多比m场 for(i=1;i<=n;i++){ REad(i,0); if(a[i][0]==4)cnt1++; } for(i=1;i<=n;i++){ REad(i,1); if(a[i][1]==4)cnt2++; if(a[i][1]==1)J.push_back(i); else if(a[i][1]==2)HK.push_back(i); else if(a[i][1]==3)W.push_back(i); else if(a[i][1]==4)YYY.push_back(i); else if(a[i][1]==5)E.push_back(i); } for(i=1;i<=n;i++)b[i][0]=Read(); for(i=1;i<=n;i++)b[i][1]=Read(); int now; for(i=1;i<=n;i++){ now=i; if(a[i][0]==1)add(s,now,b[i][0]+cnt1);//YYY只能给J加命 else add(s,now,b[i][0]); ad(i); } for(i=1;i<=n;i++){ now=i+n; if(a[i][1]==1)add(now,tt,b[i][1]+cnt2); else add(now,tt,b[i][1]); } printf("%d",Dinic()); return 0; } ```