省队集训DAY2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/clover_hxy/article/details/72822988

T1

图片标题

题解

假设我们枚举数列{ai}中长度为len的区间,那么如何判断两个数列可以匹配呢?
对于提取的数列从小到大排序,{bi}从大到小排序,然后两两配对,如果所有的都满足 >=h ,那么就可以匹配。应该算是贪心吧。
这样做的时间复杂度是 O(nlenloglen)
还是上面的思想,我们如何快速判断呢?
假设我们确定了{ai}提取出的区间数列{ci},对于数列{bi}中的每一个数我们找到{ci}中最靠左的可以与之配对的数的位置pos,那么对于每个位置我们要求 pos<=x 。设 t=xpos ,对于数列b我们用线段树对于每个位置维护t,如果区间中的最小值 <0 ,那么当前方案不合法。
如何动态的维护?我们提取出最靠前的区间,然后按照上述方法预处理。
每次的操作就是删去一个数再加入一个数。
有影响的区间就是两个数中较小的数作为pos的最靠前的位置(因为有重复的数,或者数比较紧密,有可能有的数一个位置都没有控制到,所以我们直接在b数组中二分 b[i]+x<h 最靠左的位置)为左端点,较大的数作为pos的最靠右的位置( b[i]+x>=h 最靠右的位置)为端点的区间。
如果插入的数大,剔除的数小,区间+1
如果插入的数小,剔除的数大,区间-1

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define N 150003
using namespace std;
int n,m,h,b[N],c[N],a[N],pos[N],cnt;
struct data{
    int x,delta;
}tr[N*4];
int cmp(int x,int y)
{
    return x>y;
}
int find(int x)
{
    int ans=m+1; int l=1; int r=m;
    while (l<=r) {
        int mid=(l+r)/2;
        if (a[mid]+x>=h) ans=min(ans,mid),r=mid-1;
        else l=mid+1;
    }
    return ans;
}
void update(int now)
{
    tr[now].x=min(tr[now<<1].x,tr[now<<1|1].x);
}
void build(int now,int l,int r)
{
    if (l==r) {
        tr[now].x=pos[l];
        return;
    }
    int mid=(l+r)/2;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void change(int now,int v)
{
    tr[now].x+=v;
    tr[now].delta+=v;
}
void pushdown(int now)
{
    if (tr[now].delta) {
        change(now<<1,tr[now].delta);
        change(now<<1|1,tr[now].delta);
        tr[now].delta=0;
    }
}
void query(int now,int l,int r,int ll,int rr,int v)
{
    if (ll>rr) return;
    if (ll<=l&&r<=rr) {
        change(now,v);
        return;
    } 
    int mid=(l+r)/2;
    pushdown(now);
    if (ll<=mid) query(now<<1,l,mid,ll,rr,v);
    if (rr>mid) query(now<<1|1,mid+1,r,ll,rr,v);
    update(now);
}
int getl(int x)
{
    int l=1; int r=m; int ans=m+1;
    while (l<=r) {
        int mid=(l+r)/2;
        if (b[mid]+x<h) ans=min(ans,mid),r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int getr(int x)
{
    int l=1; int r=m; int ans=0;
    while (l<=r) {
        int mid=(l+r)/2;
        if (b[mid]+x>=h) ans=max(ans,mid),l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int main()
{
    freopen("pair.in","r",stdin);
    freopen("pair.out","w",stdout);
    scanf("%d%d%d",&n,&m,&h);
    for (int i=1;i<=m;i++) scanf("%d",&b[i]);
    for (int i=1;i<=n;i++) scanf("%d",&c[i]);
    sort(b+1,b+m+1,cmp);
    if (n<=1000) {
        for (int i=1;i+m-1<=n;i++) {
            for (int j=i;j<=i+m-1;j++) a[j-i+1]=c[j];
            sort(a+1,a+m+1);
            bool pd=true;
            for (int j=1;j<=m;j++)
             if (a[j]+b[j]<h) {
                pd=false;
                break;
             }
            cnt+=(int)pd;
        }
        printf("%d\n",cnt);
        return 0;
    }
    for (int i=1;i<=m;i++) a[i]=c[i];
    sort(a+1,a+m+1);
    for (int i=1;i<=m;i++) pos[i]=find(b[i]);
    for (int i=1;i<=m;i++) pos[i]=i-pos[i];
   // for (int i=1;i<=m;i++) cout<<pos[i]<<" "; 
   // cout<<endl;
    build(1,1,m);
    if (tr[1].x>=0) cnt++;
    for (int i=m+1;i<=n;i++) {
        int t=i-m;
        if (c[i]<c[t]) {
            int x=getl(c[i]);
            int y=getr(c[t]);
            query(1,1,m,x,y,-1);
        } 
        if (c[i]>c[t]) {
            int x=getl(c[t]);
            int y=getr(c[i]);
            query(1,1,m,x,y,1);
        }
        if (tr[1].x>=0) cnt++;
    }
    printf("%d\n",cnt);
}

T2

这里写图片描述
这里写图片描述

题解

三维计算几何。
这道题应该有两种做法,一种是将地面旋转到X-Y平面上,相应的所有的点都进行坐标变换。但是我不大会坐标变换所以弃掉了。
第二种利用三维计算几何的知识,求出所有光源与凸多面体顶点连线与平面的交点,然后在平面上做凸包,最后凸包的面积就是答案。
那么如何求交点呢?

黑色的框表示投影平面,N是平面的法向量。红色的点是光源,紫色的点事凸多面体的一个顶点。两个点所在的向量与平面的交点p即为所求。
我们知道点加向量=点,那么如果我们能够求出 l2 那么问题就解决了。
l1 可以计算出来,设 K=|l2||l1|=|l3||l4| 。问题就转化成了求向量 l1 和法向量起点-光源两条向量在法向量上投影的比值。投影的长度?其实就是向量与法向量的点积。
这样我们就可以求出交点p。
给出三个点如何确定一个平面,一般平面的存储都是利用法向量,设给出的点是 p1,p2,p3 ,那么法向量就是向量 p2p1,p3p1 的叉积。注意三维的叉积上向量。
那么三维平面中的向量如何判断左右关系呢?左叉右与右叉左得到向量与法向量的方向是不同的,所以可以用求出向量与法向量正反向来判断。
如何求面积?还是划分成三角形,三角形的两条边向量求叉积得到向量,该向量的长度/2即为答案。向量长度?点积相乘再开平方。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
struct data{
    double x,y,z;
    data(double X=0,double Y=0,double Z=0){
        x=X,y=Y,z=Z;
    }
}p1,p2,p3,p[303],sun,st[303];
data operator +(data a,data b){ return data(a.x+b.x,a.y+b.y,a.z+b.z); } 
data operator -(data a,data b){ return data(a.x-b.x,a.y-b.y,a.z-b.z); }
data operator *(data a,double t){ return data(a.x*t,a.y*t,a.z*t); }
data cross(data a,data b){ return data(a.y*b.z-a.z*b.y,a.z*b.x-a.x*b.z,a.x*b.y-a.y*b.x); }
bool operator ==(data a,data b) { return a.x==b.x&&a.y==b.y&&a.z==b.z; }
int cmp(data a,data b) { return a.x<b.x||a.x==b.x&&a.y<b.y||a.x==b.x&&a.y==b.y&&a.z<b.z; }
struct line{
    data u,v;
    line(data a=data(),data b=data()) {
       u=a,v=b;
    }
}N; int n,m,top;
double dot(data a,data b){
    return a.x*b.x+a.y*b.y+a.z*b.z;
}
bool onright(data a,data b,data c){
    data t=cross(a-b,b-c);
    return dot(t,N.v)>0;
}
double area(data a,data b)
{
    data c=cross(a,b);
    return fabs(sqrt(dot(c,c)))/2;
}
int main()
{
    freopen("shadow.in","r",stdin);
    freopen("shadow.out","w",stdout);
    scanf("%lf%lf%lf",&p1.x,&p1.y,&p1.z);
    scanf("%lf%lf%lf",&p2.x,&p2.y,&p2.z);
    scanf("%lf%lf%lf",&p3.x,&p3.y,&p3.z);
    N=line(p1,cross(p2-p1,p3-p1));
    data t=cross(p3-p1,p2-p1);
    scanf("%lf%lf%lf",&sun.x,&sun.y,&sun.z);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {
        data a; scanf("%lf%lf%lf",&a.x,&a.y,&a.z);
        data b=a-sun;
        double k=dot(N.v,N.u-sun)/dot(N.v,b);
        //cout<<k<<endl;
        p[i]=sun+(b*k);
    }
    sort(p+1,p+n+1,cmp);
    n=unique(p+1,p+n+1)-p-1;
    m=0;
    //for (int i=1;i<=n;i++) printf("%.2lf %.2lf %.2lf\n",p[i].x,p[i].y,p[i].z);
    for (int i=1;i<=n;i++) {
        while (m>=2&&onright(p[i],st[m],st[m-1])) m--;
        st[++m]=p[i];
    }
    int k=m;
    for (int i=n;i>=1;i--) {
        while (m>=k&&onright(p[i],st[m],st[m-1])) m--;
        st[++m]=p[i];
    }
    if (n>1) m--;
    double ans=0;
    for (int i=3;i<=m;i++) ans+=area(st[i]-st[1],st[i-1]-st[1]);
    printf("%.2lf\n",ans);
}

猜你喜欢

转载自blog.csdn.net/clover_hxy/article/details/72822988
今日推荐