题目描述 Description
|
天上红绯在游戏中扮演敏剑,对于⾼攻击低防御的职业来说,爆发⼒显得⾮常重要,为此,她准备学习n个技能,每个技能都有2个学习⽅向:物理攻击和魔法攻击。对于第i个技能,如果选择物理攻击⽅向,会增加ap_i的爆发⼒,如果选择魔法攻击的⽅向,会增加ad_i的爆发⼒。此外,还有⼀些combo,⼀个combo由两个技能组成,对于第i个combo,如果他们都是魔法攻击,会额外增加AD_i的爆发⼒,如果他们都是物理攻击,会额外增加AP_i的爆发⼒,如果他们不属于同⼀类型,会减少AX_i的爆发⼒。她找到了你,请你帮她选择技能的类型,产⽣最⼤的爆发⼒
|
输入描述 Input Description
|
第⼀⾏2个正整数n,m,表⽰技能的个数和combo的个数接下来n⾏每⾏2个正整数,描述⼀个技能的ap_i,ad_i接下来m⾏每⾏5个正整数u,v,AD_i,AP_i,AX_i,描述⼀个combo,表⽰技能u和技能v产⽣combo |
输出描述 Output Description
|
⼀⾏,⼀个整数,表⽰最⼤的爆发⼒
|
样例输入 Sample Input
|
2 1
50 5 10 20 1 2 100 50 50 |
样例输出 Sample Output
|
125
|
数据范围及提示 Data Size & Hint
|
测试点编号
n m 1 5 5 2 50 100 3 100000 0 4 500000 0 5 1000 3000 6 1000 3000 7 1000 3000 8 10000 40000 9 10000 40000 10 10000 40000 对于所有的测试点,确保不会爆long long |
根据套路,这是一道最小割的题,至于建图,依据经验,我们很容易想到对于每一个点i,s向i连一条物理攻击的边,i向t连一条魔法攻击的边,这肯定是没有问题的。之后我们考虑combo问题,回顾一下题意:如果他们都是魔法攻击,会额外增加AD_i的爆发⼒,如果他们都是物理攻击,会额外增加AP_i的爆发⼒,如果他们不属于同⼀类型,会减少AX_i的爆发⼒。首先肯定能想到comboi一定要与ui与vi有联系。考虑本题中割的意思,对于一个点i,割物理攻击的边表示i要使用魔法攻击,割魔法攻击的边表示i要使用物理攻击,而割的目的是为了使s与t不联通,砍掉一条通路才可以。对于一个combo,只有两个技能都用物理了才能启用APi的输出,于是我们尝试ui,vi分别向combo点连一条Api的双向边,combo再向t连一条APi的边。这样就能很好的解决问题。对于魔法攻击类似,所以对于每一个combo要开两个点。剩下AXi的关系那就很显然是ui与vi连一条双向边了,容量为AXi.
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<cstring> #include<queue> using namespace std; typedef long long LL; #define mem(a,b) memset(a,b,sizeof(a)) inline LL read() { LL x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=500010,maxm=40010; const LL oo=999999999; LL ad[maxn],ap[maxn],AD[maxm],AP[maxm],AX[maxm]; int n,m,u[maxn],v[maxn]; const int maxN=90010,maxM=400010; struct Edge { int u,v,next;LL f; Edge() {} Edge(int _1,int _2,LL _3,int _4): u(_1),v(_2),f(_3),next(_4) {} }e[2*maxM]; struct Dinic { int first[maxN],dis[maxN],cur[maxN],a,b,c,ce,N,s,t; bool vis[maxN];queue <int> Q; void addEdge(int a,int b,LL c) { e[++ce]=Edge(a,b,c,first[a]);first[a]=ce; e[++ce]=Edge(b,a,0,first[b]);first[b]=ce; } void init_build() { mem(first,-1);ce=-1; s=n+2*m+1;t=n+2*m+2; for(int i=1;i<=n;i++)addEdge(s,i,ad[i]),addEdge(i,t,ap[i]); for(int i=1;i<=m;i++) { addEdge(s,n+2*i-1,AD[i]);addEdge(n+2*i,t,AP[i]); addEdge(u[i],v[i],AX[i]);addEdge(v[i],u[i],AX[i]); addEdge(n+2*i-1,u[i],oo);addEdge(n+2*i-1,v[i],oo); addEdge(u[i],n+2*i,oo); addEdge(v[i],n+2*i,oo); } N=n+2*m+2; } bool bfs() { mem(dis,42);mem(vis,0); while(Q.size())Q.pop(); dis[s]=0;vis[s]=1;Q.push(s); while(Q.size()) { int now=Q.front();Q.pop(); for(int i=first[now];i!=-1;i=e[i].next) if(e[i].f && !vis[e[i].v]) { Q.push(e[i].v); dis[e[i].v]=dis[now]+1; vis[e[i].v]=1; } } return vis[t]; } LL dfs(int x,LL a) { if(x==t || a==0)return a; LL flow=0,tmp; for(int& i=cur[x];i!=-1;i=e[i].next) if(dis[x]+1==dis[e[i].v] && (tmp=dfs(e[i].v,min(a,e[i].f)))>0) { e[i].f-=tmp;e[i^1].f+=tmp; a-=tmp;flow+=tmp; if(a==0)break; } return flow; } LL maxflow() { LL flow=0; while(bfs()) { for(int i=1;i<=N;i++)cur[i]=first[i]; flow+=dfs(s,oo); } return flow; } }fyh; void solve1() { LL ans=0; for(int i=1;i<=n;i++)ans+=max(ad[i],ap[i]); printf("%lld ",ans); return; } void solve2() { fyh.init_build(); LL sum=0; for(int i=1;i<=n;i++)sum+=(ad[i]+ap[i]); for(int i=1;i<=m;i++)sum+=(AD[i]+AP[i]); printf("%lld",sum-fyh.maxflow()); } int main() { n=(int)read();m=(int)read(); for(int i=1;i<=n;i++)ap[i]=read(),ad[i]=read(); for(int i=1;i<=m;i++)u[i]=(int)read(),v[i]=(int)read(),AD[i]=read(),AP[i]=read(),AX[i]=read(); if(m==0)solve1(); else solve2(); return 0; }