#505. 「LibreOJ β Round」ZQC 的游戏
![](https://images2018.cnblogs.com/blog/1252014/201803/1252014-20180324082355336-1815456499.png)
输入格式
第一行一个正整数 T(1≤T≤100)T (1\le T \le 100)T(1≤T≤100),表示测试数据的组数。
对于每组测试数据,第一行两个正整数 n,m n, mn,m。
接下来 n nn 行,每行四个整数 x,y,w,r x, y, w, rx,y,w,r,其中第一个玩家是 ZQC。
接下来 m mm 行,每行三个整数 x,y,w x, y, wx,y,w。
输出格式
如果方案存在,输出一行 ZQC! ZQC!
,否则输出一行 qaq
。
样例
样例输入
2
3 2
0 0 1 10
10 0 1 10
20 0 1 10
5 0 2
15 0 4
3 2
0 0 1 10
10 0 1 10
20 0 1 10
5 0 2
15 0 5
样例输出
ZQC! ZQC!
qaq
//一开始看成了有没有方案使得其他玩家的质量比zqc大 //但是其实是有没有方案使得没有其他玩家的质量比zqc大 //题目里的那个aij是 任意 非负整数 //我们要让别人超不过zqc去,所以,让zqc把他能吃的都吃了 //别的人呢? //我们不能让他们吃的比zqc重, //也就是说,他们的初始质量加上他们增加的质量<=zqc的质量 //那么我们可以知道他们最多可以吃多少 //所以,源点向这些人连边,容量为他们最多能增加的重量 //然后,让这些人向剩下的那些在他们范围内的食物球连边长为inf的边 //食物向汇点连他们质量的边 //这样,源点连出去的边是限制那些人增加的质量的,保证了他们的质量不会超过zqc //那么,在这张图上跑最大流,会有两种情况: // ①流量不等于所有食物的质量,也就是说在不超过zqc质量的限制下他们不能把食物吃完, // 但是题目要求把食物吃完,所以他们的质量一定会有超过zcq的,那么zcq就输了 // ②流量等于所有事物的质量,因为源点连向他们的边对他们有限制,所以他们把食物吃完了,而且质量还没超过zqc,那么zqc赢了 //本来建的图是把食物拆点,容量为他们的质量,然后第二个点向汇点连INF的边 //但是在lxt的指导下发现了不用这样,因为第二个点向汇点连的INF的边根本就不会流满,所以直接把食物连汇点就可以了 //感谢lxt dalao //我靠坑啊 //谁都够不着的食物球不被吃 //但是我的方法没有把它减出来 //破题破题破题 妹的 //好吧其实是我太菜了 //mlgb #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=1e4+5; const int M=1e5+5; const int INF=0x7fffffff; int n,m,S,T,Q; int Flow,flow,Sum,P; int head[N],num_edge; struct Edge { int v,flow,nxt; }edge[M<<1]; struct XY { int x,y,w,r; int flag; }ply[N],food[N]; inline int read() { char c=getchar();int num=0,f=1; for(;!isdigit(c);c=getchar()) f=c=='-'?-1:f; for(;isdigit(c);c=getchar()) num=num*10+c-'0'; return num*f; } inline void add_edge(int u,int v,int flow) { edge[++num_edge].v=v; edge[num_edge].flow=flow; edge[num_edge].nxt=head[u]; head[u]=num_edge; } inline bool calc(int a,int i) //计算距离,判断食物i在不在玩家a的范围内 { return (food[i].x-ply[a].x)*(food[i].x-ply[a].x)+(food[i].y-ply[a].y)*(food[i].y-ply[a].y)<=ply[a].r*ply[a].r; } inline void count(int a) //计算a可以吃多少食物 { for(int i=1;i<=m;++i) { if(food[i].flag==1) //这个球被zqc吃了 continue; if(calc(a,i)) //i在a的范围内 { add_edge(a,i+n,INF); //连边 add_edge(i+n,a,0); food[i].flag=2; //标记会被吃 } } } int dep[N]; inline bool bfs() //板子 { memset(dep,0,sizeof(dep)); queue<int> que; dep[S]=1,que.push(S); int now,v; while(!que.empty()) { now=que.front(),que.pop(); for(int i=head[now];i;i=edge[i].nxt) { if(edge[i].flow) { v=edge[i].v; if(dep[v]) continue; dep[v]=dep[now]+1; if(v==T) return 1; que.push(v); } } } return 0; } int dfs(int now,int flow) //板子 { if(now==T) return flow; int outflow=0,v,tmp; for(int i=head[now];i;i=edge[i].nxt) { if(edge[i].flow) { v=edge[i].v; if(dep[v]!=dep[now]+1) continue; tmp=dfs(v,min(flow,edge[i].flow)); if(tmp) { outflow+=tmp; flow-=tmp; edge[i].flow-=tmp; edge[i^1].flow+=tmp; if(!flow) return outflow; } } } dep[now]=0; return outflow; } int main() { Q=read(); while(Q--) { memset(head,0,sizeof(head)); num_edge=1; n=read(),m=read(); S=1,T=n+m+1; //源汇点 for(int i=1;i<=n;++i) { ply[i].x=read(), ply[i].y=read(), ply[i].w=read(), ply[i].r=read(); } Flow=0; //剩下的人可吃的食物的总质量 for(int i=1;i<=m;++i) { food[i].flag=0; food[i].x=read(), food[i].y=read(), food[i].w=read(); Flow+=food[i].w; //加上 } Sum=ply[1].w; //zqc的质量 for(int i=1;i<=m;++i) { if(calc(1,i)) //zqc可以吃第i个球 { Flow-=food[i].w; //剩下的人可以吃的减少 Sum+=food[i].w; //zqc长胖 food[i].flag=1; //这个球被吃了 } } bool flag=0; for(int i=2;i<=n;++i) { if(Sum-ply[i].w<0) //zqc把食物吃完了还是有比他重的,那他输了 { puts("qaq"); flag=1; //提前continue标记 break; } add_edge(S,i,Sum-ply[i].w), //汇点向人连边连边,容量为他不超过zqc的情况下可以吃的最大的食物量 add_edge(i,S,0); } if(flag) continue; for(int i=2;i<=n;++i) //人向食物连边 count(i); for(int i=1;i<=m;++i) if(!food[i].flag) //谁也吃不到的食物要把它减出来 Flow-=food[i].w; for(int i=1;i<=m;++i) //食物想汇点连边 { if(food[i].flag==1) //被zqc吃掉的食物不连边 continue; add_edge(i+n,T,food[i].w); //容量为食物质量 add_edge(T,i+n,0); } flow=0; while(bfs()) flow+=dfs(S,INF); if(flow==Flow) //judge puts("ZQC! ZQC!"); else puts("qaq"); } return 0; }