【noip模拟题】天神下凡(贪心)
vijos某次模拟赛原题。。。
处理出每个圆的一级祖先就行了。。。
其实没有那么麻烦,贪心即可出解。
我们将每个圆转换成线段后按左端点小右端点大的方法排序
然后维护一个栈:
对于每一个圆i
如果栈顶右端点比圆i的右端点小,则出栈,直到栈空
否则i的一级祖先就是栈顶,并且加入i到栈。
证明:
因为左端点排序,所以问题转换为找一个最小的右端点能够包含此线段
假如栈顶的右端点比当前右端点小,显然对于所有将来的线段,不可能包含将来的线段(或者说,尽管能,也不是最优解,因为最优解就是当前i)
然后如果有n个圆,那么有n+1个面,如果一个圆能被几个圆分割为两份,那么就加一个面。
所以我们将所有点的一级祖先加上长度然后看每个圆的长度是否为那么长即可。
#include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <vector> #include <map> using namespace std; typedef long long ll; #define pii pair<int, int> #define mkpii make_pair<int, int> #define pdi pair<double, int> #define mkpdi make_pair<double, int> #define pli pair<ll, int> #define mkpli make_pair<ll, int> #define rep(i, n) for(int i=0; i<(n); ++i) #define for1(i,a,n) for(int i=(a);i<=(n);++i) #define for2(i,a,n) for(int i=(a);i<(n);++i) #define for3(i,a,n) for(int i=(a);i>=(n);--i) #define for4(i,a,n) for(int i=(a);i>(n);--i) #define CC(i,a) memset(i,a,sizeof(i)) #define read(a) a=getint() #define print(a) printf("%d", a) #define dbg(x) cout << (#x) << " = " << (x) << endl #define error(x) (!(x)?puts("error"):0) #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; } #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '\t'; cout << endl inline const ll getint() { ll r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; } inline const int max(const int &a, const int &b) { return a>b?a:b; } inline const int min(const int &a, const int &b) { return a<b?a:b; } const int N=300005; struct dat { ll l, r; }a[N]; const bool cmp(const dat &a, const dat &b) { return a.l==b.l?a.r>b.r:a.l<b.l; } int n, s[N], top, fa[N]; ll sum[N]; int main() { read(n); for1(i, 1, n) { int x=getint(), r=getint(); a[i].l=x-r; a[i].r=x+r; } sort(a+1, a+1+n, cmp); for1(i, 1, n) { while(top && a[s[top]].r<a[i].r) --top; fa[i]=s[top]; s[++top]=i; } for1(i, 1, n) sum[fa[i]]+=a[i].r-a[i].l; int ans=n+1; for1(i, 1, n) if(sum[i]==a[i].r-a[i].l) ++ans; printf("%d\n", ans); return 0; }
背景
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
数据范围
对于30%数据,n<=5000
对于100%数据,1<=n<=300000,-10^9<=x[i]<=10^9,1<=r[i]<=10^9.