扫描线
简析
扫过来求和
例题1
B. 【例题2】矩形面积并 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
统计覆盖$\geq$1时的和,可以用vector+sort(精度高,复杂度低),也可以用线段树
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=205,M=805;
int n,m,tot,t[M],ls[M],rs[M],id[N],mx,rt;
db fid1[N],fid2[N],c[M];
struct A{db x,y; }a[N];
vector<A>V[N],V1[N];
inline bool cmpx(int i,int j) {
return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
return a[i].y<a[j].y;
}
void bld(int &p) {
t[p]=c[p]=0;
if(ls[p]) bld(ls[p]);
if(rs[p]) bld(rs[p]);
p=0;
}
void add(int &p,int l,int r,int x,int y,int k) {
if(x>y) return;
if(!p) p=++tot;
if(l==x&&r==y) {
t[p]+=k;
if(t[p]) c[p]=fid2[r]-fid2[l-1];
else c[p]=c[ls[p]]+c[rs[p]];
return;
}
int mid=l+r>>1;
if(y<=mid) add(ls[p],l,mid,x,y,k);
else if(x>mid) add(rs[p],mid+1,r,x,y,k);
else {
add(ls[p],l,mid,x,mid,k);
add(rs[p],mid+1,r,mid+1,y,k);
}
if(!t[p]) c[p]=c[ls[p]]+c[rs[p]];
}
int main() {
int e=0,cnt; db x1,y1,x2,y2;
while(scanf("%d",&n),n) {
printf("Test case #%d\nTotal explored area: ",++e);
m=0;
for(int i=1;i<=n;i++) {
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
a[++m]=(A){x1,y1},id[m]=m;
a[++m]=(A){x2,y2},id[m]=m;
}
sort(id+1,id+m+1,cmpy);
cnt=1; fid2[cnt]=a[id[1]].y,a[id[1]].y=cnt;
for(int i=2;i<=m;i++) {
if(a[id[i]].y!=fid2[(int)a[id[i-1]].y]) cnt++,fid2[cnt]=a[id[i]].y;
a[id[i]].y=cnt;
}
mx=cnt;
for(int i=1;i<=m;i++) {
id[i]=i;
}
sort(id+1,id+m+1,cmpx);
cnt=1; fid1[cnt]=a[id[1]].x,a[id[1]].x=cnt;
for(int i=2;i<=m;i++) {
if(a[id[i]].x!=fid1[(int)a[id[i-1]].x]) cnt++,fid1[cnt]=a[id[i]].x;
a[id[i]].x=cnt;
}
for(int i=1;i<=m;i+=2) {
V[(int)a[i].x].push_back((A){a[i].y,a[i+1].y});
V1[(int)a[i+1].x].push_back((A){a[i].y,a[i+1].y});
}
tot=0,bld(rt);
db ans=0;
for(int i=1;i<=cnt;i++) {
if(i!=1) {
ans+=c[rt]*(fid1[i]-fid1[i-1]);
}
for(A v:V[i]) {
if(v.x<v.y) add(rt,1,mx,v.x+1,v.y,1);
}
for(A v:V1[i]) {
if(v.x<v.y) add(rt,1,mx,v.x+1,v.y,-1);
}
}
for(int i=1;i<=m;i++) V[i].clear(),V1[i].clear();
printf("%.2lf\n",ans);
}
return 0;
}
例题2
C. 【例题3】圆的异或并 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
扫过去,如果是下半部分,则是并列关系,否则被包含
然后可以组成一棵树,深度为奇数的加上为偶数的剪掉
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int N=4e5+5;
int n,m,x[N],y[N],r[N],d[N],now;
struct A{int x,i,fl; };
struct B{int x,i,fl; }a[N<<1];
inline bool operator <(A i,A j) {
db y1=(db)i.x+(db)i.fl*sqrt((ll)r[i.i]*r[i.i]-(ll)(x[i.i]-now)*(x[i.i]-now));
db y2=(db)j.x+(db)j.fl*sqrt((ll)r[j.i]*r[j.i]-(ll)(x[j.i]-now)*(x[j.i]-now));
return y1<y2||y1==y2&&i.fl<j.fl;
}
set<A>s;
inline bool cmp(B i,B j) {
return i.x<j.x;
}
ll ans;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d%d%d",&x[i],&y[i],&r[i]);
a[++m]=(B){x[i]-r[i],i,1},a[++m]=(B){x[i]+r[i],i,-1};
}
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++) {
now=a[i].x;
if(a[i].fl==-1) {
s.erase((A){y[a[i].i],a[i].i,1});
s.erase((A){y[a[i].i],a[i].i,-1});
} else {
auto t=s.upper_bound((A){y[a[i].i],a[i].i,0});
if(t==s.end()) {
d[a[i].i]=1;
} else {
d[a[i].i]=((t->fl==1)?(!d[t->i]):d[t->i]);
}
s.insert((A){y[a[i].i],a[i].i,1});
s.insert((A){y[a[i].i],a[i].i,-1});
}
}
for(int i=1;i<=n;i++) {
ans+=d[i]?(ll)r[i]*r[i]:-(ll)r[i]*r[i];
}
printf("%lld\n",ans);
}
例题3
D. 矩形周长并 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
维护和面积并一样的线段树
如果加上某条线段后当前的长度增加,则答案加上差值
如果剪掉某条线段后当前的长度减小,则答案也加上差值
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+5;
int n,m,tot,t[N],ls[N],rs[N],id[N],mx,rt,fid1[N],fid2[N],c[N];
struct A{int x,y; }a[N];
vector<A>V[N],V1[N];
inline bool cmpx(int i,int j) {
return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
return a[i].y<a[j].y;
}
void bld(int &p) {
t[p]=c[p]=0;
if(ls[p]) bld(ls[p]);
if(rs[p]) bld(rs[p]);
p=0;
}
void add(int &p,int l,int r,int x,int y,int k) {
if(x>y) return;
if(!p) p=++tot;
if(l==x&&r==y) {
t[p]+=k;
if(t[p]) c[p]=fid2[r]-fid2[l-1];
else c[p]=c[ls[p]]+c[rs[p]];
// printf("%d %d %d %d %.2lf\n",l,r,x,y,c[p]);
return;
}
int mid=l+r>>1;
if(y<=mid) add(ls[p],l,mid,x,y,k);
else if(x>mid) add(rs[p],mid+1,r,x,y,k);
else {
add(ls[p],l,mid,x,mid,k);
add(rs[p],mid+1,r,mid+1,y,k);
}
if(!t[p]) c[p]=c[ls[p]]+c[rs[p]];
// printf("%d %d %d %d %.2lf\n",l,r,x,y,c[p]);
}
int main() {
// freopen("1.in","r",stdin);
while(scanf("%d",&n)!=EOF){
m=0;
for(int i=1;i<=n;i++) {
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
a[++m]=(A){x1,y1},id[m]=m;
a[++m]=(A){x2,y2},id[m]=m;
}
sort(id+1,id+m+1,cmpy);
int cnt2=1; fid2[cnt2]=a[id[1]].y,a[id[1]].y=cnt2;
// printf("%d %d\n",a[id[3]].y,a[id[4]].y);
for(int i=2;i<=m;i++) {
if(a[id[i]].y!=fid2[a[id[i-1]].y]) cnt2++,fid2[cnt2]=a[id[i]].y;
a[id[i]].y=cnt2;
}
/* for(int i=1;i<=m;i+=2) {
printf("%d %d %d %d\n",a[i].x,a[i].y,a[i+1].x,a[i+1].y);
}*/
for(int i=1;i<=m;i++) {
id[i]=i;
}
sort(id+1,id+m+1,cmpx);
int cnt1=1; fid1[cnt1]=a[id[1]].x,a[id[1]].x=cnt1;
for(int i=2;i<=m;i++) {
if(a[id[i]].x!=fid1[a[id[i-1]].x]) cnt1++,fid1[cnt1]=a[id[i]].x;
a[id[i]].x=cnt1;
}
for(int i=1;i<=m;i+=2) {
V[a[i].x].push_back((A){a[i].y,a[i+1].y});
V1[a[i+1].x].push_back((A){a[i].y,a[i+1].y});
}
tot=0,bld(rt);
int ans=0; int lst;
for(int i=1;i<=cnt1;i++) {
// lst=c[rt];
for(A v:V[i]) {
if(v.x<v.y) {
lst=c[rt];
add(rt,1,cnt2,v.x+1,v.y,1);
ans+=abs(c[rt]-lst);
}
}
// ans+=abs(c[rt]-lst);
// lst=c[rt];
for(A v:V1[i]) {
if(v.x<v.y) {
lst=c[rt];
add(rt,1,cnt2,v.x+1,v.y,-1);
ans+=abs(c[rt]-lst);
}
}
// ans+=abs(c[rt]-lst);
}
// printf("%d\n",ans);
for(int i=1;i<=cnt1;i++) V[i].clear(),V1[i].clear();
swap(cnt1,cnt2);
for(int i=1;i<=m;i++) {
swap(fid1[i],fid2[i]);
swap(a[i].x,a[i].y);
}
for(int i=1;i<=m;i+=2) {
V[a[i].x].push_back((A){a[i].y,a[i+1].y});
V1[a[i+1].x].push_back((A){a[i].y,a[i+1].y});
}
tot=0,bld(rt);
for(int i=1;i<=cnt1;i++) {
for(A v:V[i]) {
if(v.x<v.y) {
lst=c[rt];
add(rt,1,cnt2,v.x+1,v.y,1);
ans+=abs(c[rt]-lst);
}
}
for(A v:V1[i]) {
if(v.x<v.y) {
lst=c[rt];
add(rt,1,cnt2,v.x+1,v.y,-1);
ans+=abs(c[rt]-lst);
}
}
}
for(int i=1;i<=cnt1;i++) V[i].clear(),V1[i].clear();
printf("%d\n",ans);
}
return 0;
}
例题4
E. 贴海报 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
将其切成4块,求和
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=4e5+5,M=1e6+5;
int n,m,tot,t[M],ls[M],rs[M],id[N],mx,rt;
db fid1[N],fid2[N],c[M];
struct A{db x,y; }a[N];
vector<A>V[N],V1[N];
inline bool cmpx(int i,int j) {
return a[i].x<a[j].x;
}
inline bool cmpy(int i,int j) {
return a[i].y<a[j].y;
}
void bld(int &p) {
t[p]=c[p]=0;
if(ls[p]) bld(ls[p]);
if(rs[p]) bld(rs[p]);
p=0;
}
void add(int &p,int l,int r,int x,int y,int k) {
if(x>y) return;
if(!p) p=++tot;
if(l==x&&r==y) {
t[p]+=k;
if(t[p]) c[p]=fid2[r]-fid2[l-1];
else c[p]=c[ls[p]]+c[rs[p]];
// printf("%d %d %d %d %.2lf\n",l,r,x,y,c[p]);
return;
}
int mid=l+r>>1;
if(y<=mid) add(ls[p],l,mid,x,y,k);
else if(x>mid) add(rs[p],mid+1,r,x,y,k);
else {
add(ls[p],l,mid,x,mid,k);
add(rs[p],mid+1,r,mid+1,y,k);
}
if(!t[p]) c[p]=c[ls[p]]+c[rs[p]];
// printf("%d %d %d %d %.2lf\n",l,r,x,y,c[p]);
}
inline void ins(db x1,db y1,db x2,db y2) {
if(x1<x2&&y1<y2) {
a[++m]=(A){x1,y1},id[m]=m;
a[++m]=(A){x2,y2},id[m]=m;
}
}
int main() {
// freopen("1.in","r",stdin);
int cnt; db x1,y1,x2,y2,x3,y3,x4,y4;
while(scanf("%d",&n),n) {
m=0;
for(int i=1;i<=n;i++) {
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
ins(x1,y1,x3,y2);
ins(x4,y1,x2,y2);
ins(x3,y1,x4,y3);
ins(x3,y4,x4,y2);
}
sort(id+1,id+m+1,cmpy);
cnt=1; fid2[cnt]=a[id[1]].y,a[id[1]].y=cnt;
for(int i=2;i<=m;i++) {
if(a[id[i]].y!=fid2[(int)a[id[i-1]].y]) cnt++,fid2[cnt]=a[id[i]].y;
a[id[i]].y=cnt;
}
mx=cnt;
for(int i=1;i<=m;i++) {
id[i]=i;
}
sort(id+1,id+m+1,cmpx);
cnt=1; fid1[cnt]=a[id[1]].x,a[id[1]].x=cnt;
for(int i=2;i<=m;i++) {
if(a[id[i]].x!=fid1[(int)a[id[i-1]].x]) cnt++,fid1[cnt]=a[id[i]].x;
a[id[i]].x=cnt;
}
for(int i=1;i<=m;i+=2) {
V[(int)a[i].x].push_back((A){a[i].y,a[i+1].y});
V1[(int)a[i+1].x].push_back((A){a[i].y,a[i+1].y});
}
tot=0,bld(rt);
db ans=0;
for(int i=1;i<=cnt;i++) {
if(i!=1) {
ans+=c[rt]*(fid1[i]-fid1[i-1]);
}
for(A v:V[i]) {
if(v.x<v.y) add(rt,1,mx,v.x+1,v.y,1);
}
for(A v:V1[i]) {
if(v.x<v.y) add(rt,1,mx,v.x+1,v.y,-1);
}
}
for(int i=1;i<=m;i++) V[i].clear(),V1[i].clear();
printf("%.0lf\n",ans);
}
return 0;
}
例题5
F. 三角形面积并 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
将交点和顶点切开,则都是一大堆梯形,边和边只存在包含关系
#include <bits/stdc++.h>
#define db long double
const db eps = 1e-9, INF = 1e6;
using namespace std;
const int N = 106, M = 1005;
int n;
struct Po {
db x, y;
} a[N], b[N], c[N];
struct Li {
Po s, t;
};
inline Po operator-(Po i, Po j) { return (Po){ i.x - j.x, i.y - j.y }; }
inline Po operator*(Po i, db j) { return (Po){ i.x * j, i.y * j }; }
inline Po operator+(Po i, Po j) { return (Po){ i.x + j.x, i.y + j.y }; }
inline db operator*(Po i, Po j) { return i.x * j.y - i.y * j.x; }
inline bool equ(db x, db y) { return abs(x - y) <= eps; }
inline bool XX(Li x, Li y) {
Po A = x.s, B = x.t, C = y.s, D = y.t;
return ((C - A) * (D - A)) * ((C - B) * (D - B)) <= eps &&
((A - C) * (B - C)) * ((A - D) * (B - D)) <= eps;
}
inline Po Crs(Li x, Li y) {
Po A = x.s, B = x.t, C = y.s, D = y.t;
db s1 = abs((C - A) * (D - A)), s2 = abs((C - B) * (D - B));
return A + (B - A) * (s1 / (s1 + s2));
}
set<db> s;
void ins(Li x, Li y) {
if (XX(x, y) && !equ(0, (x.s - x.t) * (y.s - y.t))) {
Po t = Crs(x, y);
s.insert(t.x);
}
}
inline bool cmp(Po i, Po j) { return i.x < j.x; }
vector<Po> V;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%Lf%Lf", &a[i].x, &a[i].y);
scanf("%Lf%Lf", &b[i].x, &b[i].y);
scanf("%Lf%Lf", &c[i].x, &c[i].y);
if (a[i].x > c[i].x)
swap(a[i], c[i]);
if (a[i].x > b[i].x)
swap(a[i], b[i]);
if (b[i].x > c[i].x)
swap(b[i], c[i]);
s.insert(a[i].x);
s.insert(b[i].x);
s.insert(c[i].x);
}
for (int i = 1; i < n; i++) {
Li l1 = (Li){ a[i], b[i] }, l2 = (Li){ a[i], c[i] }, l3 = (Li){ b[i], c[i] };
for (int j = i + 1; j <= n; j++) {
Li l4 = (Li){ a[j], b[j] }, l5 = (Li){ a[j], c[j] }, l6 = (Li){ b[j], c[j] };
ins(l1, l4), ins(l1, l5), ins(l1, l6);
ins(l2, l4), ins(l2, l5), ins(l2, l6);
ins(l3, l4), ins(l3, l5), ins(l3, l6);
}
}
db sum = 0, ans = 0, lst = 0;
for (db j : s) {
ans += (j - lst) * sum * 0.5;
Li l = (Li){ (Po){ j, -INF }, (Po){ j, INF } };
V.clear();
for (int i = 1; i <= n; i++) {
if (a[i].x <= j && j <= c[i].x) {
Po t1 = Crs(l, (Li){ a[i], c[i] }), t2;
if (t1.x <= b[i].x && !equ(a[i].x, b[i].x)) {
t2 = Crs(l, (Li){ a[i], b[i] });
} else {
t2 = Crs(l, (Li){ b[i], c[i] });
}
db l = t1.y, r = t2.y;
if (l > r)
swap(l, r);
V.push_back((Po){ l, 1 });
V.push_back((Po){ r, -1 });
}
}
sort(V.begin(), V.end(), cmp);
sum = 0;
int res = 0;
db Lst;
for (auto i : V) {
if (i.y == 1) {
if (res == 0)
Lst = i.x;
res++;
} else {
res--;
if (res == 0)
sum += i.x - Lst;
}
}
if (j != *s.begin())
ans += sum * (j - lst) * 0.5;
lst = j;
}
printf("%.2Lf\n", ans-eps);
return 0;
}
例题6
G. 布娃娃 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
每次区间加,单点求第K大,扫描线+权值线段树
#include<bits/stdc++.h>
#define ll long long
const int p=19921228;
using namespace std;
const int N=1e5+5,M=6e6+5;
int n,a[N],b[N],L[N],R[N],mx,c[M],fid[N];
struct A{int x,y,fl; };
vector<A>V;
inline void Get(int *f) {
int Padd,Pfirst,Pmod,Pprod;
scanf("%d%d%d%d",&Padd,&Pfirst,&Pmod,&Pprod);
f[1]=Pfirst%Pmod;
for(int i=2; i<=n; i++)
f[i]=((ll)f[i-1]*Pprod+Padd+i)%Pmod;
}
inline bool cmp(A i,A j) {
return i.x<j.x||i.x==j.x&&i.fl<j.fl;
}
void add(int p,int l,int r,int x,int k) {
c[p]+=k;
if(l==r) return;
int mid=l+r>>1;
if(x<=mid) add(p<<1,l,mid,x,k);
else add(p<<1|1,mid+1,r,x,k);
}
int kth(int p,int l,int r,int k) {
if(l==r) return l;
int mid=l+r>>1,d=c[p<<1|1];
if(d>=k) return kth(p<<1|1,mid+1,r,k);
return kth(p<<1,l,mid,k-d);
}
int main() {
scanf("%d",&n);
Get(a),Get(b),Get(L),Get(R);
for(int i=1;i<=n;i++) fid[i]=b[i];
sort(fid+1,fid+n+1);
for(int i=1;i<=n;i++) b[i]=lower_bound(fid+1,fid+n+1,b[i])-fid;
for(int i=1;i<=n;i++) {
if(L[i]>R[i]) swap(L[i],R[i]);
V.push_back((A){L[i],b[i],1});
V.push_back((A){R[i]+1,b[i],-1});
V.push_back((A){a[i],i,2});
}
sort(V.begin(),V.end(),cmp);
int ans=0;
for(A v:V) {
if(v.fl==2) {
if(c[1]>=v.y) {
ans=(ans+fid[kth(1,1,n,v.y)])%p
}
} else {
add(1,1,n,v.y,v.fl);
}
}
printf("%d\n",ans);
return 0;
}
例题7
H. 立方体覆盖 - 「计算几何」第5章 扫描线 - 金牌导航 - 课程 - YbtOJ
多了一维,多枚举1维
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=205,M=805;
int n,m,tot,t[M],ls[M],rs[M],rt,fid1[N],mx1,mx2,mx3,fid2[N],fid3[N],c[M];
struct A{int x,y,z; }a[N];
struct B{int x1,y1,x2,y2,fl; };
vector<B>V1[N];
struct C{int x,y,fl; };
void bld(int &p) {
t[p]=c[p]=0;
if(ls[p]) bld(ls[p]);
if(rs[p]) bld(rs[p]);
p=0;
}
void add(int &p,int l,int r,int x,int y,int k) {
if(!p) p=++tot;
if(l==x&&r==y) {
t[p]+=k;
if(t[p]) c[p]=fid3[r]-fid3[l-1];
else c[p]=c[ls[p]]+c[rs[p]];
return;
}
int mid=l+r>>1;
if(y<=mid) add(ls[p],l,mid,x,y,k);
else if(x>mid) add(rs[p],mid+1,r,x,y,k);
else {
add(ls[p],l,mid,x,mid,k);
add(rs[p],mid+1,r,mid+1,y,k);
}
if(!t[p]) c[p]=c[ls[p]]+c[rs[p]];
}
inline bool operator <(C i,C j) {
return i.x<j.x||i.x==j.x&&i.y<j.y||i.x==j.x&&i.y==j.y&&i.fl<j.fl;
}
set<C>V2[N];
int main() {
scanf("%d",&n);
m=0;
for(int i=1;i<=n;i++) {
int x,y,z,r;
scanf("%d%d%d%d",&x,&y,&z,&r);
a[++m]=(A){x-r,y-r,z-r},fid1[m]=x-r,fid2[m]=y-r,fid3[m]=z-r;
a[++m]=(A){x+r,y+r,z+r},fid1[m]=x+r,fid2[m]=y+r,fid3[m]=z+r;
}
sort(fid1+1,fid1+m+1);
mx1=unique(fid1+1,fid1+m+1)-fid1-1;
for(int i=1;i<=m;i++) {
a[i].x=lower_bound(fid1+1,fid1+mx1+1,a[i].x)-fid1;
}
sort(fid2+1,fid2+m+1);
mx2=unique(fid2+1,fid2+m+1)-fid2-1;
for(int i=1;i<=m;i++) {
a[i].y=lower_bound(fid2+1,fid2+mx2+1,a[i].y)-fid2;
}
sort(fid3+1,fid3+m+1);
mx3=unique(fid3+1,fid3+m+1)-fid3-1;
for(int i=1;i<=m;i++) {
a[i].z=lower_bound(fid3+1,fid3+mx3+1,a[i].z)-fid3;
}
for(int i=1;i<=m;i+=2) {
V1[a[i].x].push_back((B){a[i].y,a[i].z,a[i+1].y,a[i+1].z,1});
V1[a[i+1].x].push_back((B){a[i].y,a[i].z,a[i+1].y,a[i+1].z,-1});
}
ll ans=0,sum=0;
for(int i=1;i<mx1;i++) {
for(B j:V1[i]) {
if(j.fl==-1) {
V2[j.x1].erase((C){j.y1,j.y2,1});
V2[j.x2].erase((C){j.y1,j.y2,-1});
} else {
V2[j.x1].insert((C){j.y1,j.y2,1});
V2[j.x2].insert((C){j.y1,j.y2,-1});
}
}
bld(rt),tot=0;
sum=0;
for(int j=1;j<=mx2;j++) {
for(C k:V2[j]) {
if(k.x<k.y) add(rt,1,mx3,k.x+1,k.y,k.fl);
}
if(j<mx2) sum+=(ll)(fid2[j+1]-fid2[j])*c[rt];
}
ans+=(ll)(fid1[i+1]-fid1[i])*sum;
}
printf("%lld\n",ans);
return 0;
}