T1
题解
假设我们枚举数列{ai}中长度为len的区间,那么如何判断两个数列可以匹配呢?
对于提取的数列从小到大排序,{bi}从大到小排序,然后两两配对,如果所有的都满足
这样做的时间复杂度是
还是上面的思想,我们如何快速判断呢?
假设我们确定了{ai}提取出的区间数列{ci},对于数列{bi}中的每一个数我们找到{ci}中最靠左的可以与之配对的数的位置pos,那么对于每个位置我们要求
如何动态的维护?我们提取出最靠前的区间,然后按照上述方法预处理。
每次的操作就是删去一个数再加入一个数。
有影响的区间就是两个数中较小的数作为pos的最靠前的位置(因为有重复的数,或者数比较紧密,有可能有的数一个位置都没有控制到,所以我们直接在b数组中二分
如果插入的数大,剔除的数小,区间+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即为所求。
我们知道点加向量=点,那么如果我们能够求出
这样我们就可以求出交点p。
给出三个点如何确定一个平面,一般平面的存储都是利用法向量,设给出的点是
那么三维平面中的向量如何判断左右关系呢?左叉右与右叉左得到向量与法向量的方向是不同的,所以可以用求出向量与法向量正反向来判断。
如何求面积?还是划分成三角形,三角形的两条边向量求叉积得到向量,该向量的长度/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);
}