【分析】
我的第一反应是枚举n/2的高度H。因为这就能算答案了。
比赛的时候其实没有想到太多,感觉H太小的时候答案会很大,H太大的时候也会很大,貌似是满足三分的性质,然后一交,80分。我思考了一下,可能是函数的图像还存在平行于x轴的部分,所以就改成了三分的另一种写法:二分然后比较斜率。然后就过了。
正解如下
p=(n+1)/2
我们发现ansa=| a[1]−abs(1−p)−H |+| a[2]−abs(2−p)−H |+……+| a[n]−abs(n−p)−H |,我们发现a[i]-abs(i-p)是一个定值,那我们设c[i]=a[i]-abs(i-p)。那么ansa=| c[1]−H |+| c[2]−H |+……+| c[n]−H |。稍微有一点数学常识的人应该知道,取最中间的值就行了。ansb也是如此。但是它们需要在同一个数组中sort后操作。
【代码】
#include
using namespace std;
#define ll long long
#define M 300005
ll a[M],b[M],q[2*M];
int n,z,sz;
ll ans;
int main(){
scanf("%d",&n);
z=(n+1)/2;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++){
q[++sz]=a[i]-abs(z-i);
q[++sz]=b[i]-abs(z-i);
}
sort(q+1,q+sz+1);
for(int i=1;i<=n;i++){
ans+=abs(q[n]+abs(z-i)-a[i]);
ans+=abs(q[n]+abs(z-i)-b[i]);
}
cout<return 0;
}
【分析】
说实话,在不知道我们考试的第二题其实就是D题时,我怀疑是不是我的智商早早地下线了。
我将放出三段代码。
由于我的代码比较丑,是一段通过分治思想来模拟括号匹配的dfs。权当抛砖引玉吧。
【代码1】
#include
using namespace std;
#define ll long long
#define M 300005
#define oo 2e9+5
int n;
struct node{
ll x,y;
}p[M];
ll ans;
bool cmp(node a,node b){
if(a.x!=b.x)return a.xreturn a.y>b.y;
}
void dfs(int id){
ll l=p[id].x;
ll r=p[id].y;
ll R=l;
bool f=1;
int cnt=0;
for(int i=id+1;i<=n;i++){
ll nl=p[i].x;
ll nr=p[i].y;
if(nr<=r){
if(nlcontinue;
else if(nl>R)f=0,R=nr;
else if(nl==R)R=nr;
dfs(i);
ans++;
cnt++;
}
else break;
}
if(cnt>0){
if(f&&R==r)ans++;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
ll d,r;
cin>>d>>r;
p[i].x=d-r;
p[i].y=d+r;
}
sort(p+1,p+n+1,cmp);
p[0].x=-oo;
p[0].y=oo;
dfs(0);
cout<1<return 0;
}
这意味dalao先将半径进行从小到大排序,然后将圆一个一个塞入数轴。塞入之前对答案进行更新。这样做为什么是对的呢?因为题目的圆是不会相交的,半径比它小的圆要么在大圆的里面,要么就在外面,在里面我们就需要知道小圆是否将大圆的直径填满。
每个圆都有l和r,那这就转化成一个区间更新和区间查询的问题了。用什么数据结构来实现呢?线段树是一个不错的选择。
接下来是一位叫WAonce的dalao的线段树做法。
【代码2】
#include
using namespace std;
#define M 300005
struct node{
int x,r;
bool operator <(const node &A)const{
return rint P[M<<1];
int tree[M<<3],m;
map<int,int>mp;
void up(int p){
tree[p]=tree[p<<1]&tree[p<<1|1];
}
void down(int p){
tree[p<<1]|=tree[p];
tree[p<<1|1]|=tree[p];
}
void update(int L,int R,int p=1,int l=1,int r=m){
if(L==l&&R==r){
tree[p]=1;
return;
}
int mid=l+r>>1;
if(R<=mid)update(L,R,p<<1,l,mid);
else if(L>mid)update(L,R,p<<1|1,mid+1,r);
else {
update(L,mid,p<<1,l,mid);
update(mid+1,R,p<<1|1,mid+1,r);
}
up(p);
}
bool query(int L,int R,int p=1,int l=1,int r=m){
down(p);
if(L==l&&R==r)return tree[p];
int mid=l+r>>1;
if(R<=mid)return query(L,R,p<<1,l,mid);
if(L>mid)return query(L,R,p<<1|1,mid+1,r);
return query(L,mid,p<<1,l,mid)&query(mid+1,R,p<<1|1,mid+1,r);
}
int main(){
int n,k=0;
scanf("%d",&n);
int ans=n+1;
for(int i=1;i<=n;i++){
scanf("%d %d",&s[i].x,&s[i].r);
P[++k]=s[i].x-s[i].r;
P[++k]=s[i].x+s[i].r;
}
sort(P+1,P+k+1);
m=unique(P+1,P+k+1)-P-1;
for(int i=1;i<=m;i++)mp[P[i]]=i;
sort(s+1,s+n+1);
for(int i=1;i<=n;i++){
bool cnt=query(mp[s[i].x-s[i].r]+1,mp[s[i].x+s[i].r]);
if(cnt)ans++;
update(mp[s[i].x-s[i].r]+1,mp[s[i].x+s[i].r]);
}
printf("%d\n",ans);
return 0;
}
接下来一名神秘的dalao(当然不能告诉你是我啦)的代码则是看到问题的本质才能写出来的。它直接用栈模拟括号匹配。
【代码3】
#include
using namespace std;
#define M 300005
int stk[M],sum[M];
int n,top,ans;
struct node{
int l,r;
}e[M];
bool cmp(node a,node b){
if(a.l!=b.l)return a.lreturn a.r>b.r;
}
int main(){
scanf("%d",&n);
ans=n+1;
for(int i=1;i<=n;i++){
int d,r;
scanf("%d %d",&d,&r);
e[i]=(node){d-r,d+r};
}
sort(e+1,e+n+1,cmp);
for(int i=1;i<=n;i++){
while(top&&e[stk[top]].r<=e[i].l){
if(e[stk[top]].r-e[stk[top]].l==sum[top])ans++;
sum[top]=0;
top--;
}
sum[top]+=e[i].r-e[i].l;
stk[++top]=i;
}
while(top){
if(e[stk[top]].r-e[stk[top]].l==sum[top])ans++;
sum[top]=0;
top--;
}
printf("%d\n",ans);
return 0;
}
(未完待续)