题目链接:
补一张图
我们尝试把圆上的扇形转化成直线上的矩形——我们维护[1,2*m]的区间,那么每个能产生贡献的子区间的长度*第K大的半径的平方的总和就是answer了。
怎么转化呢?左端点为a1+m+1,右端点为a2+m。为什么要+m?因为原先的范围是[-m,m]的,所以整体右移。为什么左端点要+1?因为我们维护的是区间,所以这里的每一个下标表示的是以该position为右端点,长度为1的区间。
我们先按照半径长度从大到小排序,如果一个区间覆盖数量超过K个,就不需要再处理了。(优化时间复杂度)
之后就是线段树操作了。我们在更改的同时求出答案。(其实分开写也行,就是要注意因为我们乘上的系数使然,所以区间必须也是当前的修改区间)
minn表示该区间的所有子区间覆盖量的min,maxx是该区间的所有子区间的覆盖量的max。
注意我们的siz是由左右子区间合并而来的。所以产生贡献之后,记得赋值为0,这样就不会对它的父亲区间产生贡献了。
代码如下:
#include#include #include #include #include #define MAXN 2000010using namespace std;int n,m,k;long long ans=0;struct Node{int l,r,c;}node[MAXN];struct Node2{int l,r,tag,minn,maxx,siz;}t[MAXN<<2];inline bool cmp(struct Node x,struct Node y){return x.c>y.c;}inline int ls(int x){return x<<1;}inline int rs(int x){return x<<1|1;}inline void push_up(int x){ t[x].maxx=max(t[ls(x)].maxx,t[rs(x)].maxx); t[x].minn=min(t[ls(x)].minn,t[rs(x)].minn); t[x].siz=t[ls(x)].siz+t[rs(x)].siz;}inline void build(int x,int l,int r){ t[x].l=l,t[x].r=r; if(l==r) {t[x].siz=1;return;} int mid=(l+r)>>1; build(ls(x),l,mid); build(rs(x),mid+1,r); push_up(x);}inline void solve(int x,int k){ t[x].tag+=k; t[x].minn+=k; t[x].maxx+=k;}inline void push_down(int x){ int l=t[x].l,r=t[x].r; if(t[x].tag) { solve(ls(x),t[x].tag); solve(rs(x),t[x].tag); t[x].tag=0; }}inline int update_query(int x,int ll,int rr){ int l=t[x].l,r=t[x].r; if(t[x].minn>=k) return 0; if(ll<=l&&r<=rr) { if(t[x].maxx =k-1) { int cur_ans=t[x].siz; t[x].siz=0; t[x].minn++; return cur_ans; } int cur_ans=0; push_down(x); cur_ans+=update_query(ls(x),ll,rr); cur_ans+=update_query(rs(x),ll,rr); push_up(x); return cur_ans; } push_down(x); int mid=(l+r)>>1; int cur_ans=0; if(ll<=mid) cur_ans+=update_query(ls(x),ll,rr); if(mid node[i].r) { cur_ans+=update_query(1,node[i].l,m*2); cur_ans+=update_query(1,1,node[i].r); } ans+=1ll*cur_ans*node[i].c*node[i].c; //printf("i=%d ans=%lld\n",i,ans); } printf("%lld\n",ans); return 0;}