天神下凡题解

 
   
题目描述
Czy找到宝藏获得屠龙宝刀和神秘秘籍!现在他要去找经常ntr他的Jmars报仇……


Czy学会了一招“堕天一击”,他对一个地点发动堕天一击,地面上就会留下一个很大的圆坑。圆坑的周围一圈能量太过庞大,因此无法通过。所以每次czy发动技能都会把地面分割。Jmars拥有好大好大的土地,几十眼都望不到头,所以可以假设土地的大小是无限大。现在czy对他发动了猛烈的攻击,他想知道在泽宇攻击之后他的土地被切成几份了?

Czy毕竟很虚,因此圆心都在x坐标轴上。另外,保证所有圆两两之间不会相交。


输入
输入第一行为整数n,表示czy放了n次堕天一击。

接下来n行,每行两个整数x[i],r[i]。表示在坐标(x[i] , 0)放了一次堕天一击,半径为r[i]。

输出
输出一行,表示地面被分割成几块。

样例输入
4
7 5
-9 11
11 9
0 20
样例输出
6
 
   

 

 

 

考试的时候打了n^2的dp,只拿到了30分部分分。

一个解法:

  扫描线(其实打完了发现也就是暴力模拟),先按左端点小的排序,如果左端点相同则右端点大的在前,这样可以保证每个小圆被大圆包含,本身每个圆对答案贡献是1,但是有可能一个大圆被许多小圆分割成两部份,对答案贡献需要加1,所以O(n)枚举排序后的每个圆,如果当前圆左边界和下一个圆左边界相同则说明这个圆可以被分割(但这时要注意倒数第二个圆),将这个圆压入栈。如果这个圆的右端点和栈顶圆的右端点相同则说明栈顶的大圆可以被分割,对答案贡献加1,。如果这个圆的右端点和下一个圆的左端点没有相连,则栈顶的大圆无法被彻底分割,出栈。

最后的num为被彻底分割大圆做出的贡献,本身每个圆都对答案有1个贡献,且所有圆外面也是一部份,所以最终答案应为num+n+1

好像大神的解法是线段树,不过蒟蒻太弱了,没有想出来,就打了个暴力模拟QAQ,膜大佬Orzzzz

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 __attribute__((optimize("O3")))int read() {
 8     int  s=0,f=1;
 9     char ch=getchar();
10     while(ch>'9'||ch<'0') {
11         if(ch=='-')
12             f=-1;
13         ch=getchar();
14     }
15     while(ch>='0'&&ch<='9') {
16         s=(s<<1)+(s<<3)+(ch^48);
17         ch=getchar();
18     }
19     return s*f;
20 }
21 struct oo {
22     int l,r;
23 } cir[300005];
24 int n;
25 __attribute__((optimize("O3")))bool cmp(oo a,oo b) {
26     if(a.l<b.l) {
27         return 1;
28     }
29     if(a.l>b.l) {
30         return 0;
31     }
32     if(a.r>b.r) {
33         return 1;
34     }
35     return 0;
36 }
37 int stack[300005],tail;
38 __attribute__((optimize("O3")))int main() {
39     //freopen("god8.in","r",stdin);
40     n=read();
41     for(int i=1; i<=n; i++) {
42         int x=read(),r=read();
43         cir[i].l=x-r;
44         cir[i].r=x+r;
45     }
46     sort(cir+1,cir+1+n,cmp);
47     int ans=0;
48     for(int i=1; i<=n; i++) {
49         if(cir[i].l==cir[i+1].l&&i+1!=n) {
50             stack[++tail]=i;
51             continue;
52         }
53         if(cir[i].r==cir[stack[tail]].r) {
54             ans++;
55             tail--;
56         }
57         if(cir[i].r!=cir[i+1].l) {
58             if(tail) {
59                 tail--;
60             }
61         }
62     }
63     printf("%d\n",n+ans+1);
64     return 0;
65 }

 

转载于:https://www.cnblogs.com/forevergoodboy/p/7252777.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值