C
回滚莫队。
f[i]表示存在[f[i],a[i]]的极大连续段。
cnt[f[i]]对f[i]计数。
个人理解回滚莫队主要是分为向右和向左扩展两部分,这两部分的处理往往不同。向右扩展时维护f和cnt,向左扩展时可直接维护cnt。
#include<bits/stdc++.h>
using namespace std ;
const int maxn = 3e5 + 10 ;
int a[maxn] ;
int belong[maxn] ;
struct node
{
int l , r , id ;
bool operator <(const node &s) const
{
return (belong[l] != belong[s.l]) ? belong[l] < belong[s.l] : r < s.r ;
}
} q[maxn] ;
struct Mo
{
int n , m , block ;
long long ans[maxn] ;
int L[maxn] ;
int R[maxn] ;
int dp[maxn] ;
int t[maxn] ; //不用清空的随便用的数组
int cnt[maxn] ;
int cnt2[maxn] ;
long long res ;
void init()
{
res = 0 ;
block = sqrt(n) ;
for(int i = 1 ; i <= n ; i ++) belong[i] = (i - 1) / block + 1 ;
for(int i = 1 ; i <= n ; i ++)
{
int j = i ;
while(j + 1 <= n && belong[j + 1] == belong[j]) j ++ ;
for(int k = i ; k <= j ; k ++)
{
L[belong[k]] = i ;
R[belong[k]] = j ;
}
}
for(int i = 1 ; i <= m ; i ++) ans[i] = 0 ;
}
void remove(int i) //去标记
{
res = 0 ;
for(int j = -1 ; j <= 1 ; j ++)
{
dp[a[i] + j] = 0 ;
cnt[a[i] + j] = 0 ;
cnt2[a[i] + j] = 0 ;
}
}
void solve()
{
int l = 1 , r = 0 ;
sort(q + 1 , q + m + 1) ;
int lst = -1 ;
for(int i = 1 ; i <= m ; i ++)
{
//同一块内的询问直接暴力
if(belong[q[i].l] == belong[q[i].r])
{
for(int j = q[i].l ; j <= q[i].r ; j ++)
{
t[a[j] - 1] = 0 ;
t[a[j]] = 0 ;
t[a[j] + 1] = 0 ;
}
for(int j = q[i].l ; j <= q[i].r ; j ++)
{
int tmp = t[a[j] - 1] + 1 - t[a[j]] ;
ans[q[i].id] += tmp ;
t[a[j]] = max(t[a[j]] , t[a[j] - 1] + 1) ;
}
continue ;
}
//来到了一个新的块,直接初始化左右端点
if(lst != belong[q[i].l])
{
for(int i = l ; i <= r ; i ++) remove(i) ;
l = R[belong[q[i].l]] + 1 ;
r = l - 1 ;
lst = belong[q[i].l] ;
res = 0 ;
}
//单调地移动右端点
while(r < q[i].r)
{
r ++ ;
int tmp = dp[a[r] - 1] + 1 - dp[a[r]] ;
res += tmp ;
int tt0 = a[r] - dp[a[r]] + 1 ;
dp[a[r]] = max(dp[a[r]] , dp[a[r] - 1] + 1) ;
int tt = a[r] - dp[a[r]] + 1 ;
if(tmp > 0)
{
//3 4 2 3 4
if(tt0 <= a[r]) cnt[tt0] -- ;
cnt[tt] ++ ;
}
cnt2[a[r]] ++ ;
}
//移动左端点
vector<pair<int , int>> v ;
for(int j = q[i].l ; j <= l - 1 ; j ++)
{
v.push_back({a[j] , cnt[a[j]]}) ;
v.push_back({a[j] + 1 , cnt[a[j] + 1]}) ;
}
long long tmp_res = res ;
int tmp_l = l ;
while(tmp_l > q[i].l)
{
tmp_l -- ;
int tmp = cnt[a[tmp_l] + 1] ;
res += tmp ;
cnt[a[tmp_l]] += cnt[a[tmp_l] + 1] ;
cnt[a[tmp_l] + 1] = 0 ;
if(cnt2[a[tmp_l]] == 0) cnt[a[tmp_l]] ++ , res ++ ;
cnt2[a[tmp_l]] ++ ;
}
ans[q[i].id] = res ;
//回滚
while(tmp_l < l)
{
cnt2[a[tmp_l]] -- ;
tmp_l ++ ;
}
for(auto u : v) cnt[u.first] = u.second ;
res = tmp_res ;
}
for(int i = 1 ; i <= m ; i ++) cout << ans[i] << '\n' ;
}
} mo ;
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
cin >> mo.n >> mo.m ;
for(int i = 1 ; i <= mo.n ; i ++) cin >> a[i] ;
mo.init() ;
for(int i = 1 ; i <= mo.m ; i ++)
{
cin >> q[i].l >> q[i].r ;
q[i].id = i ;
}
mo.solve() ;
return 0 ;
}
/* stuff you should look for
* long long
* array bounds
* init
* ios
* special cases (n=1?)
* do smth instead of nothing and stay organized
* WRITE STUFF DOWN
* DON'T GET STUCK ON ONE APPROACH
* DON'T GET STUCK ON ONE PROBLEM
*/
/*
5 2
1 2 3 2 4
1 3
2 4
*/
E
枚举第i个点,然后再枚举j,k,找到j比k离i更近,k比j离i更近的若干的角度即可。就四种情况。
需要加上0,2*pi作为边界。
超级好写,一发就过了,还以为细节很多。
#include<bits/stdc++.h>
using namespace std ;
#define vec point
typedef double db ; //卡精度就换long double
const db eps = 1e-6 ; //调参
const db pi = acos(-1.0) ;
int sgn(db x)
{
if(x < -eps) return -1 ;
else if (x > eps) return 1 ;
else return 0 ;
}
int cmp(db x , db y)
{
return sgn(x - y) ;
}
void print(int num , db x)
{
cout << fixed << setprecision(num) << x << '\n' ;
}
struct point
{
db x , y ;
point(){}
point(db x2 , db y2)
{
x = x2 , y = y2 ;
}
point operator + (const point& s)const{return (point){x + s.x , y + s.y} ;}
point operator - (const point& s)const{return (point){x - s.x , y - s.y} ;}
point operator * (const db& k)const{return (point){x * k , y * k} ;}
point operator / (const db& k)const{return (point){x / k , y / k} ;}
bool operator < (point b) const
{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x ;
}
bool equal(point p2)
{
return cmp(x , p2.x) == 0 && cmp(y , p2.y) == 0 ;
}
db get_angle(){return atan2(y , x) ;} //极角
db sq(db x) {return x * x ;}
db dis(point p) //很吃精度,可能需要1e-10或更小的eps
{
return sqrtl(sq(x - p.x) + sq(y - p.y)) ; //check sqrt or sqrtl
}
db len()
{
return sqrtl(sq(x) + sq(y)) ;
}
db len2()
{
return sq(x) + sq(y) ;
}
point unit()
{
db w = len() ;
return (point){x / w , y / w} ;
}
vec rotate_left() //向量逆时针旋转90度
{
return vec(-y , x) ;
}
vec rotate_right() //向量顺时针旋转90度
{
return vec(y , -x) ;
}
point move(db r) //原点沿该点代表的向量方向移动r
{
db l = len() ;
if(sgn(l) == 0) return *this ;
else return point(x * r / l , y * r / l) ;
}
vec rotate(db ang) //ang是弧度制,逆时针旋转
{
return vec({x * cos(ang) - y * sin(ang) , y * cos(ang) + x * sin(ang)}) ;
}
} ;
db cross(vec s , vec t){return s.x * t.y - s.y * t.x ;} // 叉积
db dot(vec s , vec t){return s.x * t.x + s.y * t.y ;} // 点积
db rad(vec p1 , vec p2){ return atan2(cross(p1 , p2) , dot(p1 , p2)) ;} // 有向弧度
int in_mid(db k1 , db k2 , db k3) // k3 在 [k1,k2] 内
{
return cmp(k1 , k3) * cmp(k2 , k3) <= 0 ;
}
int in_mid(point p1 , point p2 , point p3)
{
return in_mid(p1.x , p2.x , p3.x) && in_mid(p1.y , p2.y , p3.y) ;
}
int counter_clockwise(point p1 , point p2 , point p3) // p1 p2 p3 逆时针 1 顺时针 -1 否则 0
{
return sgn(cross(p2 - p1 , p3 - p1)) ;
}
struct line
{
point s , e ; // 点s在直线上,方向向量是e ,半平面在e左侧
db ang ; //在运算时注意设置ang的值!
line() {}
line(point s2 , point e2)
{
s = s2 , e = e2 ;
ang = e.get_angle() ;
}
bool operator < (const line& l1)const{return ang < l1.ang ;}
int onRight(point p){return sgn(cross(e , p - s)) < 0 ;} //点p在直线右侧
point projection(point p) //p在该直线上的投影点
{
db t = dot(e , p - s) / e.len() ;
return s + e.move(t) ;
}
point reflection(point p) //p关于直线的对称点
{
point p2 = projection(p) ;
vec v = p2 - p ;
return p + v * 2 ;
}
db dis_point(point p) //点到直线的距离
{
db t = dot(e , p - s) / e.len() ;
point pp = s + e.move(t) ;
return p.dis(pp) ;
}
db dis_segment_point(point p) //点到线段的距离
{
point p1 = s ; //线段p1->p2
point p2 = s + e ;
point p3 = projection(p) ;
if(in_mid(p1 , p2 , p3)) return p.dis(p3) ;
else return min(p.dis(p1) , p.dis(p2)) ;
}
bool on_segment(point p) //p在线段s->s+e上
{
return sgn((p.dis(s) + p.dis(s + e)) - s.dis(s + e)) == 0 ;//
}
int direction(point p2) //p0 = s , p1 = s + e 判断p2与向量p0->p1的位置关系
{
point p0 = s ;
point p1 = s + e ;
db res = cross(p1 - p0 , p2 - p0) ;
if(sgn(res) > 0) return 1 ; //p0->p1逆时针旋转得到p0->p2的方向
else if(sgn(res) < 0) return 2 ; //p0->p1顺时针旋转得到p0->p2的方向
else
{
db res2 = dot(p1 - p0 , p2 - p0) ;
if(on_segment(p2)) return 5 ; //p2在线段p0->p1上
else if(sgn(res2) < 0) return 3 ; //p0->p1与p0->p2方向相反
else return 4 ; //p0->p1与p0->p2方向相同
}
}
int Parallel_Orthogonal(line l2) //判断两条直线平行、正交
{
if(sgn(cross(e , l2.e)) == 0) return 2 ; //平行
else if(sgn(dot(e , l2.e)) == 0) return 1 ; //正交
else return 0 ;
}
point line_intersect(point s1 , point t1 , point s2 , point t2) // 直线交点(点+向量表示直线)
{
db x = cross(t2 , s1 - s2) / cross(t1 , t2) ;
return s1 + t1 * x ;
}
point line_intersect(line l2) //直线交点(点+向量表示直线)
{
assert(sgn(cross(e , l2.e)) != 0) ;
return line_intersect(s , e , l2.s , l2.e) ;
}
point segment_intersect(line l2) //线段s->s+e与线段l2.s->l2.s+l2.e的交点
{
assert(sgn(cross(e , l2.e)) != 0) ;
return line_intersect(s , e , l2.s , l2.e) ;
}
bool can_segment_intersect(line l2) //线段s->s+e与线段l2.s->l2.s+l2.e
{
int t1 = sgn(cross(e , l2.s - s)) ;
int t2 = sgn(cross(e , (l2.s + l2.e) - s)) ;
int t3 = sgn(cross(l2.e , s - l2.s)) ;
int t4 = sgn(cross(l2.e , (s + e) - l2.s)) ;
if(on_segment(l2.s) || on_segment(l2.s + l2.e)
|| l2.on_segment(s) || l2.on_segment(s + e)) //端点在另一条线段上的情况
return true ;
else if(t1 * t2 < 0 && t3 * t4 < 0) return true ;
else return false ;
}
db segment_segment_dis(line l2) //线段s->s+e与线段l2.s->l2.s+l2.e
{
if(can_segment_intersect(l2)) return 0.0 ;
else
{
db res = min({s.dis(l2.s) , s.dis(l2.s + l2.e) , (s + e).dis(l2.s) , (s + e).dis(l2.s + l2.e)}) ;
point t = l2.projection(s) ;
if(l2.on_segment(t)) res = min(res , s.dis(t)) ;
point t2 = l2.projection(s + e) ;
if(l2.on_segment(t2)) res = min(res , (s + e).dis(t2)) ;
point t3 = projection(l2.s) ;
if(on_segment(t3)) res = min(res , l2.s.dis(t3)) ;
point t4 = projection(l2.s + l2.e) ;
if(on_segment(t4)) res = min(res , (l2.s + l2.e).dis(t4)) ;
return res ;
}
}
line trans(db r) //半平面u向内平移r
{
line v ;
v.s = s + e.rotate_left().move(r) ;
v.e = e ;
v.ang = e.get_angle() ; //注意设置ang的值
return v ;
}
} ;
struct Polygon
{
vector<point> pp ;
Polygon() {}
Polygon(int n)
{
pp.resize(n) ;
}
int size() const
{
return pp.size() ;
}
void resize(int n)
{
pp.resize(n) ;
}
point operator [](int id) const
{
if(id < 0 || id >= size()) assert(false) ;
else return pp[id] ;
}
point &operator [](int id)
{
return pp[id] ;
}
// 凸包
// Andrew算法
// 按照x优先的顺序排序(坐标从小到大)
// 从第一个点开始遍历,如果下一个点在栈顶的两个元素所连成直线的左边,那么就入栈
// 否则如果在右边,说明凸包有更优的方案,上次的点出栈,并直到新点在栈顶两个点所在的直线的左边为止
// 这样求得下凸包,类似方法求得上凸包。
void Convex_Hull(vector<point> P , vector<point> &res , int flag = 1) // flag = 0 不严格(存在三点共线) flag = 1 严格(不存在三点共线)
{
int n = P.size() ;
res.resize(0) ;
res.resize(n * 2) ;
sort(P.begin() , P.end()) ;
int now = -1 ;
for(int i = 0 ; i < (int)P.size() ; i ++)
{
while(now > 0 && sgn(cross(res[now] - res[now - 1] , P[i] - res[now - 1])) < flag) now -- ;
res[++ now] = P[i] ;
}
int pre = now ;
for(int i = n - 2 ; i >= 0 ; i --)
{
while(now > pre && sgn(cross(res[now] - res[now - 1] , P[i] - res[now - 1])) < flag) now -- ;
res[++ now] = P[i] ;
}
res.resize(now) ;
}
// 半平面交
// 排序增量算法
// 1.以逆时针为正方向,建边。(输入方向不确定时,可用叉乘求面积看正负得知输入的顺逆方向。)
// 2.对线段根据极角排序。
// 3.去除极角相同的情况下,位置在右边的边。
// 4.用双端队列储存线段集合L,遍历所有线段。
// 5.判断该线段加入后对半平面交的影响,(对双端队列的头部和尾部进行判断,因为线段加入是有序的。)。
// 6.如果某条线段对于新的半平面交没有影响,则从队列中剔除掉。
// 7.最后剩下的线段集合L,即使最后要求的半平面交。
int Half_Plane_Intersection(vector<line>L , vector<point> &res) //L.push_back({p[i] , p[(i + 1) % m] - p[i]}) ;
{
int n = L.size() ;
sort(L.begin() , L.end()) ;
int l = 0 , r = 0 ;
vector<point> p(n) ;
vector<line> q(n) ;
q[0] = L[0] ;
for(int i = 1 ; i <= n - 1 ; i ++)
{
while(l < r && L[i].onRight(p[r - 1])) r -- ;
while(l < r && L[i].onRight(p[l])) l ++ ;
q[++ r] = L[i] ;
if (sgn(cross(q[r].e , q[r - 1].e)) == 0)
{
r -- ;
if(!q[r].onRight(L[i].s)) q[r] = L[i] ;
}
if(l < r) p[r - 1] = q[r - 1].line_intersect(q[r]) ;
}
while(l < r && q[l].onRight(p[r - 1])) r -- ;
if(r - l <= 1) return 0 ; // 交不存在
res.clear() ;
p[r] = q[l].line_intersect(q[r]) ;
for(int i = l ; i <= r ; i ++) res.push_back(p[i]) ;
return 1 ;
}
db perimeter(vector<point> A) // 多边形用 vector<point> 表示 , 逆时针
{
db ans = 0 ;
for(int i = 0 ; i < (int)A.size() ; i ++) ans += A[i].dis(A[(i + 1) % (int)A.size()]) ;
return ans ;
}
db area() // 多边形用 vector<point> 表示 , 逆时针
{
db ans = 0 ;
for(int i = 0 ; i < (int)pp.size() ; i ++) ans += cross(pp[i] , pp[(i + 1) % (int)pp.size()]) ;
return fabs(ans) / 2 ;
}
bool is_convex(vector<point> A) // 多边形用 vector<point> 表示 , 逆时针
{
int n = A.size() ;
assert(n >= 3) ;
bool flag = true ;
for(int i = 0 ; i < n ; i ++)
{
int lst = (i - 1 + n) % n ;
int nxt = (i + 1) % n ;
if(sgn(cross(A[lst] - A[i] , A[nxt] - A[i])) > 0) flag = false ;
}
return flag ;
}
int contain_point(point p0) // 不一定是凸的 2 内部 1 边界 0 外部
{
int n = pp.size() ;
int res = 0 ;
for(int i = 0 ; i < n ; i ++)
{
point u = pp[(i - 1 + n) % n] ;
point v = pp[i] ;
line l ;
l.s = u ;
l.e = v - u ;
if(l.on_segment(p0)) return 1 ;
if(cmp(u.y , v.y) > 0) swap(u , v) ;
if(cmp(u.y , p0.y) >= 0 || cmp(v.y , p0.y) < 0) continue ;
if(sgn(cross(u - v , p0 - v)) < 0) res ^= 1 ;
}
return res << 1 ;
}
db Convex_Diameter()
{
//原理是凸多边形的凸函数性质
int now = 0 ;
int n = pp.size() ;
db res = 0 ;
for(int i = 0 ; i < n ; i ++)
{
now = max(now , i) ;
while(true)
{
db k1 = pp[i].dis(pp[now % n]) ;
db k2 = pp[i].dis(pp[(now + 1) % n]) ;
res = max({res , k1 , k2}) ;
if(cmp(k2 , k1) > 0) now ++ ;
else break ;
}
}
return res ;
}
Polygon line_cut_convex(point p1 , point p2)
{
//在已有凸多边形基础上新加一个半平面p1->p2
//问之后的凸多边形
int n = pp.size() ;
line l ;
l.s = p1 ;
l.e = p2 - p1 ;
Polygon res ;
for(int i = 0 ; i < n ; i ++)
{
int w1 = counter_clockwise(p1 , p2 , pp[i]) ;
int w2 = counter_clockwise(p1 , p2 , pp[(i + 1) % n]) ;
if(w1 >= 0) res.pp.push_back(pp[i]) ;
line l2 ;
l2.s = pp[i] ;
l2.e = pp[(i + 1) % n] - pp[i] ;
if(w1 * w2 < 0) res.pp.push_back(l.line_intersect(l2)) ;
}
return res ;
}
} ;
struct circle
{
point o ;
db r ;
db area()
{
return pi * r * r ;
}
int inside(point p2)
{
return cmp(r , o.dis(p2)) ;
}
circle get_circle(point p1 , point p2 , point p3) //三点确定一个圆
{
db a1 = p2.x - p1.x , b1 = p2.y - p1.y , c1 = (a1 * a1 + b1 * b1) / 2 ;
db a2 = p3.x - p1.x , b2 = p3.y - p1.y , c2 = (a2 * a2 + b2 * b2) / 2 ;
db d = a1 * b2 - a2 * b1 ;
point o = (point){p1.x + (c1 * b2 - c2 * b1) / d , p1.y + (a1 * c2 - a2 * c1) / d} ;
return (circle){o , p1.dis(o)} ;
}
vector<point> Tangent_Point_Circle(point p) //沿圆的逆时针方向给出切点,点在圆上也给出两个
{
//相似三角形
assert(cmp(p.dis(o) , r) >= 0) ;
db a = (p - o).len() ;
assert(sgn(a) >= 0) ;
db b = r * r / a ;
db c = sqrt(max(db(0.0) , r * r - b * b)) ;
point k = (p - o).unit() ;
point m = o + k * b ;
point v = k.rotate_left() * c ;
return {m - v , m + v} ;
}
int check_pos_circle_circle(circle c1 , circle c2) //返回两个圆的公切线数量,即位置关系
{
//根据圆心之间的距离及两个圆的半径判断
if(cmp(c1.r , c2.r) < 0) swap(c1 , c2) ;
db d = c1.o.dis(c2.o) ;
int w1 = cmp(d , c1.r + c2.r) ;
int w2 = cmp(d , c1.r - c2.r) ;
if(w1 > 0) return 4 ; //相离
else if(w1 == 0) return 3 ; //外切
else if(w2 > 0) return 2 ; //相交
else if(w2 == 0) return 1 ; //内切
else return 0 ; //内含
}
vector<point> get_circle_line(line l) //圆与直线求交点,沿l.e方向的顺序给出交点,相切给出两个
{
//投影后勾股定理
point k = l.projection(o) ;
db d = r * r - (k - o).len2() ;
if(sgn(d) < 0) return {} ;
vec v = l.e.unit() * sqrtl(max((db)0.0 , d)) ;
return {k - v , k + v} ;
}
vector<point> get_circle_circle(circle c1 , circle c2) //两个圆的交点,沿圆c1逆时针给出,相切给出两个
{
//余弦定理
int pos = check_pos_circle_circle(c1 , c2) ;
if(pos == 0 || pos == 4) return {} ;
db a = (c2.o - c1.o).len2() ;
db cosA = (c1.r * c1.r + a - c2.r * c2.r) / (2 * c1.r * sqrtl(max(a , (db)0.0))) ;
db b = c1.r * cosA ;
db c = sqrtl(max((db)0.0 , c1.r * c1.r - b * b)) ;
point k = (c2.o - c1.o).unit() ;
point m = c1.o + k * b ;
vec v = k.rotate_left() * c ;
return {m - v , m + v} ;
}
circle Incircle_of_a_Triangle(point p1 , point p2 , point p3) //三角形p1p2p3内接圆
{
//角平分线求交点,点到直线距离
line l1 ;
l1.s = p1 ;
l1.e = (p2 - p1).unit() + (p3 - p1).unit() ;
line l2 ;
l2.s = p2 ;
l2.e = (p1 - p2).unit() + (p3 - p2).unit() ;
point p = l1.line_intersect(l2) ;
line l3 ;
l3.s = p1 ;
l3.e = p2 - p1 ;
circle cc ;
cc.o = p ;
cc.r = l3.dis_point(p) ;
return cc ;
}
circle Circumscribed_Circle_of_a_Triangle(point p1 , point p2 , point p3) //三角形p1p2p3外接圆
{
//垂直平分线求交点,点到直线距离
line l1 ;
l1.s = p1 + (p2 - p1) / 2 ;
l1.e = (p2 - p1).rotate_left() ; //顺时针或逆时针旋转90度均可,因为是直线求交
line l2 ;
l2.s = p2 + (p3 - p2) / 2 ;
l2.e = (p3 - p2).rotate_left() ;
point p = l1.line_intersect(l2) ;
circle cc ;
cc.o = p ;
cc.r = p.dis(p1) ;
return cc ;
}
vector<line> Tangent_out_Circle_Circle(circle c1 , circle c2) //外切线
{
//相似三角形
int pos = check_pos_circle_circle(c1 , c2) ;
if(pos == 0) return {} ;
if(pos == 1)
{
point k = get_circle_circle(c1 , c2)[0] ;
vec v = k - c1.o ;
v = v.rotate_left() ;
line l ;
l.s = k ;
l.e = v ;
return {l} ;
}
if(cmp(c1.r , c2.r) == 0)
{
vec v = (c2.o - c1.o).rotate_left().unit() * c1.r ;
vec p1 = c1.o + v ;
line l1 ;
l1.s = p1 ;
l1.e = c2.o - c1.o ;
vec p2 = c1.o - v ;
line l2 ;
l2.s = p2 ;
l2.e = c2.o - c1.o ;
return {l1 , l2} ;
}
else
{
vec v = c2.o - c1.o ;
v = v.unit() * (c1.o.dis(c2.o) * c1.r / (c1.r - c2.r)) ;
point H = c1.o + v ;
vector<point> pp1 = c1.Tangent_Point_Circle(H) ;
line l1 ;
l1.s = H ;
l1.e = pp1[0] - H ;
line l2 ;
l2.s = H ;
l2.e = pp1[1] - H ;
return {l1 , l2} ;
}
}
vector<line> Tangent_in_Circle_Circle(circle c1 , circle c2) //内切线
{
int pos = check_pos_circle_circle(c1 , c2) ;
if(pos <= 2) return {} ;
if(pos == 3)
{
point k = get_circle_circle(c1 , c2)[0] ;
vec v = k - c1.o ;
v = v.rotate_left() ;
line l ;
l.s = k ;
l.e = v ;
return {l} ;
}
db t = c1.r / (c1.r + c2.r) * c1.o.dis(c2.o) ;
point p = c1.o + (c2.o - c1.o).unit() * t ;
vector<point> pp1 = c1.Tangent_Point_Circle(p) ;
line l1 ;
l1.s = p ;
l1.e = pp1[0] - p ;
line l2 ;
l2.s = p ;
l2.e = pp1[1] - p ;
return {l1 , l2} ;
}
vector<line> Tangent_Circle_Circle(circle c1 , circle c2) //两个圆的公切线
{
//外切线和内切线一起考虑
if(cmp(c1.r , c2.r) < 0) swap(c1 , c2) ;
vector<line> A = Tangent_out_Circle_Circle(c1 , c2) ;
vector<line> B = Tangent_in_Circle_Circle(c1 , c2) ;
for(auto u : B) A.push_back(u) ;
return A ;
}
db get_area_of_Circle_Triangle(circle c1 , point p2 , point p3)
{
//圆c1与三角形c1 p2 p3的交
point k = c1.o ;
c1.o = c1.o - k ;
p2 = p2 - k ;
p3 = p3 - k ;
int pos1 = c1.inside(p2) ;
int pos2 = c1.inside(p3) ;
line l ;
l.s = p2 ;
l.e = p3 - p2 ;
vector<point> pp = c1.get_circle_line(l) ;
if(pos1 >= 0)
{
if(pos2 >= 0) return cross(p2 , p3) / 2 ;
return c1.r * c1.r * rad(pp[1] , p3) / 2 + cross(p2 , pp[1]) / 2 ;
}
else if(pos2 >= 0)
{
return c1.r * c1.r * rad(p2 , pp[0]) / 2 + cross(pp[0] , p3) / 2 ;
}
else
{
line l ;
l.s = p2 ;
l.e = p3 - p2 ;
int pos = cmp(c1.r , l.dis_segment_point(c1.o)) ;
if(pos <= 0) return c1.r * c1.r * rad(p2 , p3) / 2 ;
return cross(pp[0] , pp[1]) / 2 + c1.r * c1.r * (rad(p2 , pp[0]) + rad(pp[1] , p3)) / 2 ;
}
}
db get_area_of_Circle_Polygon(circle c1 , vector<point> pp) //pp按照逆时针给出
{
//转成三角形和圆求有向面积交
int n = pp.size() ;
db res = 0 ;
for(int i = 0 ; i < n ; i ++)
{
point now = pp[i] ; //三角形o pp[i] pp[(i + 1) % n] 需要按照顺序给出
point nxt = pp[(i + 1) % n] ;
res += get_area_of_Circle_Triangle(c1 , now , nxt) ; //已经除2
}
return fabs(res) ;
}
db get_area_of_Circle_Circle(circle c1 , circle c2) //两个圆的相交面积
{
//扇形减去四边形面积
if(cmp(c1.r , c2.r) < 0) swap(c1 , c2) ;
int pos = check_pos_circle_circle(c1 , c2) ;
if(pos >= 3) return 0.0 ;
if(pos <= 1) return c2.area() ;
vector<point> pp = get_circle_circle(c1 , c2) ;
db S1 = fabs(2.0 * rad(pp[0] - c1.o , c2.o - c1.o)) * c1.r * c1.r / 2 ;
db S2 = fabs(2.0 * rad(pp[0] - c2.o , c1.o - c2.o)) * c2.r * c2.r / 2 ;
db S3 = fabs(cross(pp[0] - c1.o , c2.o - c1.o)) ;
return S1 + S2 - S3 ;
}
} c ;
db closest_point(vector<point> &A , int l , int r) //先按照x轴排序
{
if(r - l <= 5)
{
db res = 1e20 ;
for(int i = l ; i <= r ; i ++)
for(int j = i + 1 ; j <= r ; j ++)
res = min(res , A[i].dis(A[j])) ;
return res ;
}
int mid = (l + r) / 2 ;
db res = min(closest_point(A , l , mid) , closest_point(A , mid + 1 , r)) ;
vector<point> B ;
for(int i = l ; i <= r ; i ++)
if(cmp(fabs(A[i].x - A[mid].x) , res) <= 0)
B.push_back(A[i]) ;
sort(B.begin() , B.end() , [&](point p1 , point p2){ return cmp(p1.y , p2.y) < 0 ; }) ;
for(int i = 0 ; i < B.size() ; i ++)
for(int j = i + 1 ; j < B.size() && B[j].y - B[i].y < res ; j ++)
res = min(res , B[i].dis(B[j])) ;
return res ;
}
int main()
{
std::ios::sync_with_stdio(false) , cin.tie(0) ;
int n ;
cin >> n ;
vector<point> p(n) ;
for(int i = 0 ; i < n ; i ++) cin >> p[i].x >> p[i].y ;
vector<vector<db>> ans(n , vector<db>(n , 0.0)) ;
for(int i = 0 ; i < n ; i ++)
{
vector<db> v ;
v.push_back(0.0) ;
v.push_back(2 * pi) ;
auto solve = [&](int i , int j , int k)
{
db t = (p[k] - p[j]).get_angle() ;
db w1 = pi / 2 - t ;
db w2 = 3 * pi / 2 - t ;
db t2 = ((p[j] - p[i]) + (p[k] - p[i])).get_angle() ;
db w3 = pi / 2 - t2 ;
db w4 = 3 * pi / 2 - t2 ;
if(w1 < 0) w1 += 2 * pi ;
if(w1 > 2 * pi) w1 -= 2 * pi ;
if(w2 < 0) w2 += 2 * pi ;
if(w2 > 2 * pi) w2 -= 2 * pi ;
if(w3 < 0) w3 += 2 * pi ;
if(w3 > 2 * pi) w3 -= 2 * pi ;
if(w4 < 0) w4 += 2 * pi ;
if(w4 > 2 * pi) w4 -= 2 * pi ;
v.push_back(w1) ;
v.push_back(w2) ;
v.push_back(w3) ;
v.push_back(w4) ;
} ;
auto find = [&](db ang)
{
vector<point> pp = p ;
for(auto &u : pp) u = u.rotate(ang) ;
db mn = 1e9 ;
int id = -1 ;
for(int j = 0 ; j < n ; j ++)
if(j != i && cmp(fabs(pp[j].x - pp[i].x) , mn) < 0)
{
mn = fabs(pp[j].x - pp[i].x) ;
id = j ;
}
return id ;
} ;
for(int j = 0 ; j < n ; j ++)
for(int k = j + 1 ; k < n ; k ++)
if(j != i && k != i && j != k)
solve(i , j , k) ;
sort(v.begin() , v.end()) ;
for(int j = 0 ; j < (int)v.size() - 1 ; j ++)
{
vec now = {cos(v[j]) , sin(v[j])} ;
vec nxt = {cos(v[j + 1]) , sin(v[j + 1])} ;
vec t = (now + nxt) / 2 ;
int id = find(t.get_angle()) ;
ans[i][id] += (v[j + 1] - v[j]) / (2 * pi) ;
}
}
for(int i = 0 ; i < n ; i ++)
for(int j = 0 ; j < n ; j ++)
cout << fixed << setprecision(10) << ans[i][j] << " \n"[j == n - 1] ;
return 0 ;
//you should actually read the stuff at the bottom before submitting or in the confusion
}
/* stuff you should look for
* long long
* array bounds
* init
* ios
* special cases (n=1?)
* do smth instead of nothing and stay organized
* WRITE STUFF DOWN
* DON'T GET STUCK ON ONE APPROACH
* DON'T GET STUCK ON ONE PROBLEM
*/
/*
*/