【题目描述】
天上红绯在游戏中扮演敏剑,对于高攻击低防御的职业来说,爆发力显得非常重要,为此,她准备学习n个技能,每个技能都有2个学习方向:物理攻击和魔法攻击。对于第i个技能,如果选择物理攻击方向,会增加ap_i的爆发力,如果选择魔法攻击的方向,会增加ad_i的爆发力。此外,还有一些combo,一个combo由两个技能组成,对于第i个combo,如果他们都是魔法攻击,会额外增加AD_i的爆发力,如果他们都是物理攻击,会额外增加AP_i的爆发力,如果他们不属于同一类型,会减少AX_i的爆发力。她找到了你,请你帮她选择技能的类型,产生最大的爆发力。(说明:每个技能都必须学习)
【输入数据】
第一行2个正整数n,m,表示技能的个数和combo的个数
接下来n行每行2个正整数,描述一个技能的ap_i,ad_i
接下来m行每行5个正整数u,v,AD_i,AP_i,AX_i,描述一个combo,表示技能u和技能v产生combo
【输出数据】
一行,一个整数,表示最大的爆发力
【样例输入】
2 1 50 5 10 20 1 2 100 50 50
【样例输出】
125
【样例解释】
均选择魔法攻击,获得5+20+100=125的爆发力
【数据范围】
测试点编号 |
n |
m |
1 |
5 |
5 |
2 |
50 |
100 |
3 |
100000 |
0 |
4 |
500000 |
0 |
5-7 |
1000 |
3000 |
8-10 |
10000 |
40000 |
对于所有的测试点,确保不会爆long long
Solution
模型:最小割,最大权闭合子图
来自出题者的题解:可以先看作我们把所有的技能的收益(不包括亏损AX)都获得了,然后在这个获益中扣除最小的代价
建图:
对于每个技能,建边(S,u,ap),(u,T,ad)
对于每个combo,拆点,对点一建边(S,buf1,AD),(buf1,u,+∞),(buf1,v,+∞)
对点二建边(buf2,T,AP),(u,buf2,+∞),(v,buf2,+∞)
跑个最大流算法就行
#include<stdio.h> #include<limits.h> #include<string.h> #define LL long long #define dmin(a,b) ((a)<(b)?(a):(b)) inline int Rin(){ int x=0,c=getchar(),f=1; for(;c<48||c>57;c=getchar()) if(!(c^45))f=-1; for(;c>47&&c<58;c=getchar()) x=(x<<1)+(x<<3)+c-48; return x*f; } const int N=500010; const int M=40010; const LL LINF=LONG_LONG_MAX/3*2; LL tot(0); int n,m,S,T,ecnt=1,fst[N+M*2]; struct edge{ int u,v,nxt; LL cap,flow; }e[(6*M+2*N)*2]; inline void link(int x,int y,LL w){ e[++ecnt].u=x;e[ecnt].v=y; e[ecnt].cap=w;e[ecnt].flow=0; e[ecnt].nxt=fst[x];fst[x]=ecnt; e[++ecnt].u=y;e[ecnt].v=x; e[ecnt].cap=0;e[ecnt].flow=0; e[ecnt].nxt=fst[y];fst[y]=ecnt; } bool vis[N+M*2]; int num[N+M*2],cur[N+M*2],d[N+M*2],p[N+M*2],q[N+M*2]; void bfs(){ int hd=0,tl=1; q[0]=T;d[T]=0;vis[T]=true; while(hd^tl){ int now=q[hd++]; for(int j=fst[now];j;j=e[j].nxt) if(!vis[e[j].u] && e[j].cap>e[j].flow) vis[e[j].u]=true, d[e[j].u]=d[now]+1, q[tl++]=e[j].u; } } inline LL Agument(){ LL a=LINF; int x=T; while(x^S) a=dmin(a,e[p[x]].cap-e[p[x]].flow), x=e[p[x]].u; x=T; while(x^S) e[p[x]].flow+=a, e[p[x]^1].flow-=a, x=e[p[x]].u; return a; } LL ISAP(){ int x(S); LL flow(0); bfs(); for(int i=S;i<=T;i++)num[d[i]]++; for(int i=S;i<=T;i++)cur[i]=fst[i]; while(d[S]<T){ if(!(x^T)) flow+=Agument(), x=S; bool advanced=false; for(int j=cur[x];j;j=e[j].nxt) if(e[j].cap>e[j].flow && d[x]==d[e[j].v]+1){ advanced=true; cur[x]=j; p[e[j].v]=j; x=e[j].v; break; } if(!advanced){ int mn=T-1; for(int j=fst[x];j;j=e[j].nxt) if(e[j].cap>e[j].flow) mn=dmin(mn,d[e[j].v]); if(--num[d[x]]==0)break; num[d[x]=mn+1]++; cur[x]=fst[x]; if(x^S)x=e[p[x]].u; } } return flow; } int main(){ freopen("skill.in","r",stdin); freopen("skill.out","w",stdout); n=Rin(),m=Rin(); S=1,T=n+1+m*2+1; for(int i=1;i<=n;i++){ LL ap=Rin(),ad=Rin(); tot+=ap+ad; link(S,i+1,ap); link(i+1,T,ad); } for(int i=1;i<=m;i++){ int u=Rin(),v=Rin(); LL AP=Rin(),AD=Rin(),AX=Rin(); tot+=AP+AD; link(u+1,v+1,AX); link(v+1,u+1,AX); link(S,n+1+i,AD); link(n+1+i,u+1,LINF); link(n+1+i,v+1,LINF); link(n+1+i+m,T,AP); link(u+1,n+1+i+m,LINF); link(v+1,n+1+i+m,LINF); } printf("%I64d ",tot-ISAP()); fclose(stdin); fclose(stdout); return 0; }