BZOJ1822[JSOI2010] 冷冻波

本文针对游戏中的“冷冻波”问题进行了解析,通过计算几何方法确定角色间的可达性,并利用二分图匹配算法计算消灭所有敌方单位所需的最短时间。

luogu链接:https://www.luogu.org/problemnew/show/P4048
bzoj链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1822

冷冻波

题目描述

WJJ喜欢“魔兽争霸”这个游戏。在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵。我们认为,巫妖和小精灵都可以看成是平面上的点。

当巫妖和小精灵之间的直线距离不超过R,且巫妖看到小精灵的视线没有被树木阻挡(也就是说,巫妖和小精灵的连线与任何树木都没有公共点)的话,巫妖就可以瞬间杀灭一个小精灵。

在森林里有N个巫妖,每个巫妖释放Frozen Nova之后,都需要等待一段时间,才能再次施放。不同的巫妖有不同的等待时间和施法范围,但相同的是,每次施放都可以杀死一个小精灵。

现在巫妖的头目想知道,若从0时刻开始计算,至少需要花费多少时间,可以杀死所有的小精灵?

输入输出格式
输入格式:

输入文件第一行包含三个整数N、M、K(N,M,K<=200),分别代表巫妖的数量、小精灵的数量和树木的数量。

接下来N行,每行包含四个整数x, y, r, t,分别代表了每个巫妖的坐标、攻击范围和施法间隔(单位为秒)。

再接下来M行,每行两个整数x, y,分别代表了每个小精灵的坐标。

再接下来K行,每行三个整数x, y, r,分别代表了每个树木的坐标。

输入数据中所有坐标范围绝对值不超过10000,半径和施法间隔不超过20000。

输出格式:

输出一行,为消灭所有小精灵的最短时间(以秒计算)。如果永远无法消灭所有的小精灵,则输出-1。

输入输出样例
输入样例#1:

2 3 1
-100 0 100 3
100 0 100 5
-100 -10
100 10
110 11
5 5 10

输出样例#1:

5

题解

用计算几何求解巫妖和精灵间能否互相到达并以此建图,然后二分时间跑二分图匹配不断更新答案即可AC。

细节

注意先判断-1的情况,二分上界取maxcd*m,即冷却时间最长的巫妖单独杀死所有精灵的情况。更新时间后重新建图,超级源点到每个巫妖的边流量取time/cd[i]+1,即总时间除以冷却时间+1(因为在time=0的时候巫妖可以发射第一发Frozen Nova),其余边流量都为1。  

代码略长大家实现的时候耐心一点,其实就是计算几何+dinic没什么思维难度,主要考代码实现。

另外,洛谷上第三个点博主的裸dinic被卡T了,加个当前弧优化快了500ms,把inline去掉又快了200ms(迷。。。),bzoj无压力过。

代码
#include<bits/stdc++.h>
#define db double
#define R register
using namespace std;
const int M=50005;
const db eps=1e-8;
struct edge{int to,fl;};
struct pt{int x,y,r;};
int sig(db x){return (x>eps)-(x<-eps);}
db dis(pt a,pt b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)*1.0);}
int operator * (pt a,pt b){return a.x*b.y-a.y*b.x;}
pt operator - (pt a,pt b){return (pt){a.x-b.x,a.y-b.y};}
db dis(pt p,pt a,pt b){return abs((a-p)*(b-p)*1.0/dis(a,b));}
pt lich[M],elv[M],tree[M];
edge ed[1000005];
vector<int>x[M];
int d[M],g=0,maxcd,cd[M],s,e,n,m,k,f;
bool vis[M];
char c;
void read(int &r)
{
    r=0;f=1;c=getchar();
    while(!isdigit(c))
    {
        if(c=='-')f=-1;
        c=getchar();
    }
    while(isdigit(c))r=(r<<1)+(r<<3)+c-'0',c=getchar();
    r*=f;
}
void add(int a,int b)
{
    ed[g]=(edge){b,1};
    x[a].push_back(g++);
    ed[g]=(edge){a,0};
    x[b].push_back(g++);
}
int bfs(int s,int e)
{
    R int i;
    memset(d,0,sizeof(d));
    queue<int>dui;
    dui.push(s);
    d[s]=1;
    int f,hh,t;
    while(!dui.empty())
    {
        f=dui.front();
        dui.pop();
        for(i=x[f].size()-1;i>=0;--i)
        {
            hh=x[f][i];
            t=ed[hh].to;
            if(d[t]||!ed[hh].fl)continue;
            d[t]=d[f]+1;
            dui.push(t);
        }
    }
    return d[e];
}
int dfs(int s,int e,int minn)
{
    R int i;
    if(s==e||!minn)return minn;
    int ans=0,hh,fl,to,tmp;
    for(i=x[s].size()-1;i>=0;--i)
    {
        hh=x[s][i];
        fl=ed[hh].fl;
        to=ed[hh].to;
        if(d[to]!=d[s]+1||!fl)continue;
        tmp=dfs(to,e,min(minn-ans,fl));
        if(!tmp)continue;
        ed[hh].fl-=tmp;
        ed[hh^1].fl+=tmp;
        ans+=tmp;
        if(minn==tmp)break;
    }
    return ans;
}
int dinic()
{
    int ans=0;
    while(bfs(e,s))
    ans+=dfs(e,s,1e9);
    return ans;
}
void reb(int t)
{
    R int i;
    int hh;
    for(i=0;i<g;i+=2)
    ed[i].fl=1,ed[i+1].fl=0;
    for(i=x[0].size()-1;i>=0;--i)
    ed[x[0][i]^1].fl=t/cd[ed[x[0][i]].to]+1;
}
void in()
{
    R int i;
    read(n);read(m);read(k);
    e=n+m+1;
    for(i=1;i<=n;++i)
    read(lich[i].x),read(lich[i].y),read(lich[i].r),read(cd[i]),maxcd=max(maxcd,cd[i]);
    for(i=1;i<=m;++i)
    read(elv[i].x),read(elv[i].y);
    for(i=1;i<=k;++i)
    read(tree[i].x),read(tree[i].y),read(tree[i].r);
}
bool check(pt a,pt b)
{
    R int i;
    if(sig(dis(a,b)-a.r)>0) return 0;
    for(i=1;i<=k;++i)
    {
        if(sig(dis(a,tree[i])-tree[i].r)<0)return 0;
        if(sig(dis(b,tree[i])-tree[i].r)<0)return 0;
        if((dis(tree[i],a,b)-tree[i].r)<0)return 0;
    }
    return 1;
}
void build()
{
    R int i,j;
    for(i=1;i<=n;++i)
    add(i,0);
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j)
    if(check(lich[i],elv[j]))
    {
        add(j+n,i);
        vis[j]=1;
    }
    for(i=1;i<=m;++i)
    add(n+m+1,n+i);
    for(i=1;i<=m;++i)
    if(!vis[i])
    {
        printf("-1");
        exit(0);
    }
}
void ac()
{
    int le=0,ri=m*maxcd,mid,ans,p;
    while(le<=ri)
    {
        mid=(le+ri)>>1;
        reb(mid);
        p=dinic();
        if(p==m)
        {
            ans=mid;
            ri=mid-1;
        }
        else le=mid+1;
    }
    printf("%d",ans);
}
int main()
{
    in();build();ac();
    return 0;
}
引用中未提及P2143 [JSOI2010]巨额奖金的相关信息,但有BZOJ 1016 JSOI 2008巨额奖金的问题描述。该问题是在一个有n个区、m条干道的城市规划交通枢纽,要将部分干道改进为新型干道,使任何两个区可通过新型干道直接或间接连接,已知每条干道改进费用,求建设新型干道总费用最小的方案数,输出方案总数除以31011的模。 解题思路如下: 1. 先使用Kruskal算法求出最小生成树的权值,同时记录每种权值的边在最小生成树中使用的数量。 2. 对于每种权值的边,通过枚举其所有可能的组合情况,判断这些组合能否构成满足条件的部分生成树,统计满足条件的组合数。 3. 根据乘法原理,将每种权值边的组合数相乘,得到最终的方案数,再对31011取模。 以下是代码实现示例(Python 伪代码): ```python MOD = 31011 # 边的类 class Edge: def __init__(self, u, v, w): self.u = u self.v = v self.w = w # 并查集查找操作 def find(parent, x): if parent[x] != x: parent[x] = find(parent, parent[x]) return parent[x] # 并查集合并操作 def union(parent, rank, x, y): root_x = find(parent, x) root_y = find(parent, y) if rank[root_x] < rank[root_y]: parent[root_x] = root_y elif rank[root_x] > rank[root_y]: parent[root_y] = root_x else: parent[root_y] = root_x rank[root_x] += 1 # Kruskal算法求最小生成树 def kruskal(edges, n): edges.sort(key=lambda x: x.w) parent = [i for i in range(n + 1)] rank = [0] * (n + 1) mst_edges = [] total_weight = 0 for edge in edges: u = edge.u v = edge.v w = edge.w root_u = find(parent, u) root_v = find(parent, v) if root_u != root_v: union(parent, rank, u, v) mst_edges.append(edge) total_weight += w return mst_edges, total_weight # 统计每种权值边的方案数 def count_schemes(edges, n): mst_edges, _ = kruskal(edges, n) weight_count = {} for edge in mst_edges: if edge.w not in weight_count: weight_count[edge.w] = 0 weight_count[edge.w] += 1 total_schemes = 1 for weight, count in weight_count.items(): # 这里需要具体实现枚举组合并判断的逻辑 # 由于代码复杂,此处省略具体实现 # 假设 valid_combinations 是该权值边的有效组合数 valid_combinations = 1 total_schemes = (total_schemes * valid_combinations) % MOD return total_schemes # 主函数 def main(): # 读取输入 n, m = map(int, input().split()) edges = [] for _ in range(m): u, v, w = map(int, input().split()) edges.append(Edge(u, v, w)) # 计算方案数 schemes = count_schemes(edges, n) print(schemes) if __name__ == "__main__": main() ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值