JOI 系列乱做

「JOISC 2014 Day3」稻草人

题目描述

Solution

对于一个点 \(A\),能与其组成答案的点 \(B\),必然不存在另一点 \(C\),其在 \(A,B\) 所组成的矩形之中。
二维题目的一个常用 trick:按一维排序后作为时间轴,降为一维。
如果按 \(x\) 排序之后按 \(y\) 插入每一个点,问题变为区间最大后缀个数,这个是区间单调栈的经典。
时间复杂度 \(O(n\log^2n)\)。

Code
const int N=2e5;
struct Point {
    int x,y;
} a[N+10];
int lsh[N+10];
bool cmp(Point a,Point b) {
    return a.y<b.y;
}
ll ans;
int ma[N*4+10],cnt[N*4+10];
int calc(int p,int l,int r,int x) {
    if(l==r) return ma[p]>x;
    int mid=(l+r)>>1;
    if(ma[p*2+1]>x) return cnt[p]-cnt[p*2+1]+calc(p*2+1,mid+1,r,x);
    return calc(p*2,l,mid,x);
}
void pushup(int p,int l,int r) {
    ma[p]=ma[p*2+1],cnt[p]=cnt[p*2+1];
    int mid=(l+r)>>1;
    cnt[p]+=calc(p*2,l,mid,ma[p]);
    chkmax(ma[p],ma[p*2]);
}
void update(int p,int l,int r,int x,int v) {
    if(l==r) {cnt[p]=1,ma[p]=v;return;}
    int mid=(l+r)>>1;
    if(x<=mid) update(p*2,l,mid,x,v);
    else update(p*2+1,mid+1,r,x,v);
    pushup(p,l,r);
}
pii query(int p,int l,int r,int x,int y) {
    if(x<=l&&r<=y) return mp(cnt[p],ma[p]);
    int mid=(l+r)>>1;
    if(y<=mid) return query(p*2,l,mid,x,y);
    pii res=query(p*2+1,mid+1,r,x,y);
    res.fi+=calc(p*2,l,mid,res.se);
    chkmax(res.se,ma[p*2]);
    return res;
}
int main() {
    int n,tot=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%d %d",&a[i].x,&a[i].y);
        a[i].y++,lsh[++tot]=a[i].x;
    }
    sort(lsh+1,lsh+tot+1);
    for(int i=1;i<=n;i++) a[i].x=lower_bound(lsh+1,lsh+tot+1,a[i].x)-lsh;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) {
        ans=(ans+(ll)query(1,1,n,1,a[i].x).fi);
        update(1,1,n,a[i].x,a[i].y);
    }
    printf("%lld\n",ans);
}

「JOISC 2014 Day4」两个人的星座

题目描述

Solution

考虑枚举内公切线。
固定内公切线的一点,将每个点极角排序,初始时以内公切线为极轴,统计上方和下方的点,就可以统计答案。
考虑转极轴,因为是内公切线,那么可以快速统计答案。
注意最后每个三角形被算了 \(4\) 次,记得减去。

Code
const int N=3e4;
struct Point {
    int x,y,c;
    Point(){}
    Point(int _x,int _y,int _c=0):
        x(_x),y(_y),c(_c) {}
    void get() {scanf("%d %d %d",&x,&y,&c);}
    bool where() {return y<0||(!y&&x<0);}
};
Point operator + (Point a,Point b) {
    return Point(a.x+b.x,a.y+b.y);
}
Point operator - (Point a,Point b) {
    return Point(a.x-b.x,a.y-b.y,a.c);
}
ll operator * (Point a,Point b) {
    return 1ll*a.x*b.y-1ll*a.y*b.x;
}
bool operator < (Point a,Point b) {
    if(a.where()!=b.where()) return a.where()<b.where();
    return a*b>0;
}
Point a[N+10],b[N*2+10];
int cnt[2][3];
int main() {
    ll ans=0;
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) a[i].get();
    for(int I=1;I<=n;I++) {
        int m=0;memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++)
            if(i!=I) {
                b[++m]=a[i]-a[I];
                ++cnt[0][b[m].c];
            }
        sort(b+1,b+m+1);
        for(int i=1;i<=m;i++) b[i+m]=b[i];
        for(int i=1,j=1;i<=m;i++) {
            if(j==i) {
                --cnt[0][b[j].c],++cnt[1][b[j].c];
                j++;
            }
            while(j<i+m&&b[i]*b[j]>0) {
                --cnt[0][b[j].c],++cnt[1][b[j].c];
                j++;
            }
            --cnt[1][b[i].c];
            ll s1=1,s2=1;
            for(int k=0;k<3;k++) {
                if(k!=a[I].c) s1*=cnt[0][k],s2*=cnt[1][k];
                if(k!=b[i].c) s1*=cnt[1][k],s2*=cnt[0][k];
            }
            ans+=s1+s2;
            ++cnt[0][b[i].c];
        }
    }
    printf("%lld\n",ans/4);
}
上一篇:AT4299 [ABC128C] Switches


下一篇:Repetitions