比赛 |
难度级别:D; 运行时间限制:2000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述
|
初三年级举办了一场篮球赛,共有N个班级参加。当WZJ知道了这件事情, 已经打完了若干场比赛(WZJ一直在写Fenwich套Treap),接下来还要进行M场比赛。第i场比赛的竞争班级是ai,bi,胜者得2分,负者得0 分,若平局则两班各得1分。为了让得分尽量平均,现在给出每个班级已经得到的分数si,与接下来M场比赛的安排,请你回答得分最多的班级的得分最少是多 少。
|
输入
|
第一行为两个正整数N,M。
第二行为N个正整数si。 接下来M行每行两个正整数ai,bi。 |
输出
|
请你回答得分最多的班级的得分最少是多少。
|
输入示例
|
5 3 1 2 2 1 3 1 5 2 3 4 5 |
输出示例
|
3
|
其他说明
|
第一场比赛1赢
第二场比赛平局 第三场比赛4赢 则最后每个班级的得分都是3 1<=N<=1000 1<=M<=50000 1<=si<=100 1<=ai!=bi<=M |
题解:赤裸裸的网络流建模。
首先看到破题口"得分最多的班级的得分最少是多少"先二分答案,将问题转化为"得分为point的方案是否可行"。怎么检查可行呢?(或者说怎么样才算可行)就是强制分配这么多的得分,如果每一场比赛都成功分配了两个队的分数我们就称之为"可行"(其实算是ISAP的处理流的办法:我们先模拟大量的流,不断地修正检查获得结果。以前我都不知道= =)。当然了,这么想是很辨证的,事实上一定会有其他的解法。
继续,我们怎么分配分数并检查呢?仔细阅读这道题的特殊性:每场比赛一定会贡献两个积分,于是我们就用流来模拟积分的流动转移,最后在汇点处用容量卡每个队能获取分数的最大值。易知当且仅当每场比赛都分配出了两个积分(即最大流是比赛数量的两倍时)这样的方案是合法的。
Ps二分图还是跑Dinic吧,ISAP快不了多少:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<queue> 6 #include<cstring> 7 using namespace std; 8 const int maxn=52000+10,maxm=500000+10,inf=-1u>>1; 9 struct ISAP{ 10 struct tedge{int x,y,w,next;}adj[maxm];int ms,fch[maxn]; 11 int d[maxn],s[maxn],cur[maxn],gap[maxn],n,top; 12 void init(int n){ 13 this->n=n;ms=0;top=0; 14 memset(d,-1,sizeof(d)); 15 memset(fch,-1,sizeof(fch)); 16 return; 17 } 18 void addedge(int u,int v,int w){ 19 adj[ms]=(tedge){u,v,w,fch[u]};fch[u]=ms++; 20 adj[ms]=(tedge){v,u,0,fch[v]};fch[v]=ms++; 21 return; 22 } 23 void bfs(){ 24 queue<int>Q;Q.push(n);d[n]=0; 25 while(!Q.empty()){ 26 int u=Q.front();Q.pop(); 27 for(int i=fch[u];i!=-1;i=adj[i].next){ 28 int v=adj[i].y; 29 if(d[v]==-1) d[v]=d[u]+1,Q.push(v); 30 } 31 } return; 32 } 33 int maxflow(int S,int T){ 34 n=T;bfs();int k=S,i,flow=0; 35 for(i=0;i<=n;i++) cur[i]=fch[i],gap[d[i]]++; 36 while(d[S]<n){ 37 if(k==n){ 38 int mi=inf,pos; 39 for(i=0;i<top;i++) if(adj[s[i]].w<mi) mi=adj[s[i]].w,pos=i; 40 for(i=0;i<top;i++) adj[s[i]].w-=mi,adj[s[i]^1].w+=mi; 41 flow+=mi;top=pos;k=adj[s[top]].x; 42 } 43 for(i=cur[k];i!=-1;i=adj[i].next){ 44 int v=adj[i].y; 45 if(adj[i].w&&d[k]==d[v]+1){cur[k]=i;k=v;s[top++]=i;break;} 46 } 47 if(i==-1){ 48 int lim=n; 49 for(i=fch[k];i!=-1;i=adj[i].next){ 50 int v=adj[i].y; 51 if(adj[i].w&&d[v]<lim) lim=d[v],cur[k]=i; 52 } if(--gap[d[k]]==0) break; 53 d[k]=lim+1;gap[d[k]]++; 54 if(k!=S) k=adj[s[--top]].x; 55 } 56 } return flow; 57 } 58 }sol; 59 inline int read(){ 60 int x=0,sig=1;char ch=getchar(); 61 while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();} 62 while(isdigit(ch)) x=10*x+ch-'0',ch=getchar(); 63 return x*=sig; 64 } 65 inline void write(int x){ 66 if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x; 67 int len=0,buf[15];while(x) buf[len++]=x%10,x/=10; 68 for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return; 69 } 70 int n,m,a[maxn],b[maxn],p[maxn]; 71 bool check(int point){ 72 sol.init(n+m+3); 73 int S=n+m+1,T=n+m+2; 74 for(int i=1;i<=m;i++){ 75 sol.addedge(S,i,2); 76 sol.addedge(i,a[i]+m,inf); 77 sol.addedge(i,b[i]+m,inf); 78 } 79 for(int i=1;i<=n;i++) sol.addedge(i+m,T,point-p[i]); 80 return sol.maxflow(S,T)==m<<1; 81 } 82 void init(){ 83 int L=0,R=0;n=read();m=read(); 84 for(int i=1;i<=n;i++) p[i]=read(),L=max(L,p[i]); 85 for(int i=1;i<=m;i++) a[i]=read(),b[i]=read(); 86 R=L+(m<<1); 87 while(L<R){ 88 int M=L+R>>1; 89 if(check(M)) R=M; 90 else L=M+1; 91 } 92 write(L); 93 return; 94 } 95 void work(){ 96 return; 97 } 98 void print(){ 99 return; 100 } 101 int main(){ 102 init();work();print();return 0; 103 }