2017.5.21 COCI2013/2014 Contest#6

C Mirko和Slavko的积木

【分析】
我的第一反应是枚举n/2的高度H。因为这就能算答案了。
比赛的时候其实没有想到太多,感觉H太小的时候答案会很大,H太大的时候也会很大,貌似是满足三分的性质,然后一交,80分。我思考了一下,可能是函数的图像还存在平行于x轴的部分,所以就改成了三分的另一种写法:二分然后比较斜率。然后就过了。
正解如下
p=(n+1)/2
我们发现ansa=| a[1]abs(1p)H |+| a[2]abs(2p)H |+……+| a[n]abs(np)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 神奇的麦田怪圈

【分析】
说实话,在不知道我们考试的第二题其实就是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;
}

(未完待续)

你可能感兴趣的:(2017.5.21 COCI2013/2014 Contest#6)