LibreOJ β Round F. ZQC 的游戏【思维建图+最大流】

F. ZQC 的游戏

内存限制: 256 MiB 时间限制: 1000 ms
标准输入输出
题目描述

Agar.io 是一款流行的游戏,每个玩家在二维平面上控制一个球。

我们对游戏进行下列简化:

  • 现在有 n(1≤n≤100) n(1 \leq n \leq 100)n(1n100) 个玩家(包括 ZQC 自己),每个玩家有一个坐标 (x,y) (x,y)(x,y) 和活动半径 r(0≤x,y,r≤10000) r(0 \leq x, y, r \leq 10000)r(0x,y,r10000),当前质量 w(1≤w≤10000) w(1 \leq w \leq 10000)w(1w10000)。也就是每个玩家在一个圆里活动,包括边界
    形式化地,玩家的活动范围为 S={(a,b)∣(a−x)2+(b−y)2≤r2,a∈R,b∈R}S=\{(a,b)|(a-x)^2+(b-y)^2\leq r^2,a\in \mathbb{R},b\in \mathbb{R}\}S={(a,b)(ax)2+(by)2r2,aR,bR}
  • 还有 m(1≤m≤400) m(1 \leq m \leq 400)m(1m400) 个食物球,每个球有坐标 (xf,yf)(0≤xf,yf≤10000) (x_f, y_f)(0 \leq x_f, y_f \leq 10000)(xf,yf)(0xf,yf10000) 和质量 wf(1≤wf≤10000) w_f(1 \leq w_f \leq 10000)wf(1wf10000)
  • 每个玩家可以吃到自己活动范围内的食物球(不能吃其它玩家),并且可以只吃一部分,每个玩家吃每个食物球的量必须是非负整数,吃掉的部分会加在自身质量上。
    形式化地,用一个 n×mn\times mn×m 的矩阵 AAA 来表示吃的情况,其中 AijA_{ij}Aij 表示玩家 iii 吃食物 jjj 的量,则:
    • ∀i,j\forall i,ji,j 有 Aij∈NA_{ij}\in \mathbb{N}AijN
    • ∀i\forall ii,最终的质量 wni=wi+∑j=1mAijw_{\text{n}i}=w_i+\sum_{j=1}^m A_{ij}wni=wi+j=1mAij
    • ∀j\forall jj 有 ∑i=1nAij≤wfj\sum_{i=1}^n A_{ij}\leq {w_f}_ji=1nAijwfj
  • 由于 ZQC 非常神,她可以钦点所有玩家的行动。
    • ZQC 会将它活动范围内的所有食物球吃光。
    • 对于每个食物球,如果它在至少一个玩家的活动范围内,则它一定要被吃光。形式化地,设这个食物球编号为 jjj,则有 ∑i=1nAij=wfj\sum_{i=1}^n A_{ij}= {w_f}_ji=1nAij=wfj

问有没有一种钦点方案使得没有其它玩家的质量比 ZQC 的更大∀i,wni≤wn1\forall i,w_{\text{n}i}\leq w_{\text{n}1}i,wniwn1)?


一句话题意:问是否存在一种分配方案使得所有能被吃到的食物球都被吃光,并且满足 ZQC 是最大的玩家(之一)。

输入格式

第一行一个正整数 T(1≤T≤100)T (1\le T \le 100)T(1T100),表示测试数据的组数。
对于每组测试数据,第一行两个正整数 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的质量。设定为Sum.

那么对应我们希望其他角色吃到的物品的总质量最多为Sum-a【i】.w.

很显然问题直接贪心是不可取的,那么我们考虑网络流,将流平均分配即可。


建图方式如下:

①建立源点,连入各个物品,流为INF.

②拆点,将每个物品i连入点i+m,流为这个物品的质量.

③然后将每个物品的拆点i+m连入各个角色,流为INF(前提是这个角色能够吃到这个物品).

④然后将各个角色连入汇点,流为Sum-a【i】.w..


建好图之后跑最大流,那么最大流==所有可以被吃到的物品的质量和。

那么就表示可以平均分配,即输出ZQC! ZQC!

否则输出qaq。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
struct node
{
    int x,y,w,r;
}a[1500];
struct node2
{
    int x,y,w;
}b[1500];
struct node3
{
    int from;
    int to;
    int w;
    int next;
}e[5000000];
int vis[50000];
int head[50000];
int cur[50000];
int divv[50000];
int n,m,Sum,ss,tt,cont,ok;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
void Getmap()
{
    ss=n+2*m+1;
    tt=ss+1;
    cont=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        add(ss,i,0x3f3f3f3f);
        add(i,ss,0);
        add(i,i+m,b[i].w);
        add(i+m,i,0);
        for(int j=1;j<=n;j++)
        {
            if(((b[i].x-a[j].x)*(b[i].x-a[j].x)+(b[i].y-a[j].y)*(b[i].y-a[j].y))<=a[j].r*a[j].r)
            {
                add(i+m,j+2*m,0x3f3f3f3f);
                add(j+2*m,i+m,0);
                vis[i]=1;
            }
        }
    }
    for(int j=1;j<=n;j++)
    {
        if(Sum-a[j].w>=0)
        {
            add(j+2*m,tt,Sum-a[j].w);
            add(tt,j+2*m,0);
        }
        else ok=0;
    }
}
int makedivv()
{
    queue<int >s;
    s.push(ss);
    memset(divv,0,sizeof(divv));
    divv[ss]=1;
    while(!s.empty())
    {
        int u=s.front();
        if(u==tt)return 1;
        s.pop();
        for(int i=head[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            int w=e[i].w;
            if(w&&divv[v]==0)
            {
                divv[v]=divv[u]+1;
                s.push(v);
            }
        }
    }
    return 0;
}
int Dfs(int u,int maxflow,int tt)
{
    int ret=0;
    if(u==tt)return maxflow;
    for(int &i=cur[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        int w=e[i].w;
        if(w&&divv[v]==divv[u]+1)
        {
            int f=Dfs(v,min(maxflow-ret,w),tt);
            e[i].w-=f;
            e[i^1].w+=f;
            ret+=f;
            if(ret==maxflow)return ret;
        }
    }
    return ret;
}
void Dinic()
{
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,0x3f3f3f3f,tt);
    }
    int sum=0;
    for(int j=1;j<=m;j++)
    {
        if(vis[j]==1)
        {
            sum+=b[j].w;
        }
    }
    if(sum==ans)printf("ZQC! ZQC!\n");
    else printf("qaq\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&a[i].r);
        }
        Sum=a[1].w;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].w);
            if(((b[i].x-a[1].x)*(b[i].x-a[1].x)+(b[i].y-a[1].y)*(b[i].y-a[1].y))<=a[1].r*a[1].r)
            {
                Sum+=b[i].w;
                b[i].w=0;
            }
        }
        ok=1;
        Getmap();
        if(ok==0)
        {
            printf("qaq\n");
        }
        else Dinic();
    }
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值