JZOJ4877. 【NOIP2016提高A组集训第10场11.8】力场护盾

ZMiG面对707设置的无限大护盾,利用特殊能力找到弱点,并通过转换坐标系的方法解决攻击角度限制问题,最终找出最长的连续攻击序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

ZMiG成功粉碎了707的基因突变计划,为了人类的安全,他决定向707的科学实验室发起进攻!707并没有想到有人敢攻击她的实验室,一时间不知所措,决定牺牲电力来换取自己实验室的平安。
在实验室周围瞬间产生了一个无限大的力场护盾,它看上去无懈可击!不过ZMiG拥有惊人的双向观察能力,经过他的反复观察,找到了这个护盾的N个弱点,他本想逐一击破,却发现一股神秘力量阻止了他的行为。原来他身处力场之中,受到了两股神秘力量的影响,这两股力量来自两个不同的方向并形成了一个小于180度的角,ZMiG每次可攻击的范围都受到这两个力的影响,当他攻击了点X之后,下一次可以攻击的点必须在以X为坐标原点的情况下,这两个力方向的夹角之间(包含边界)(具体意思可看样例)
ZMiG当然想打出一串最长的Combo,所以他想问问你最多可以攻击707弱点多少

Slution

我在做这道题时并不会怎样将那两个斜的向量给去掉,所以我打了个分段暴力,只能处理两条直线在同一象限的情况……

这道题我们考虑首先要将那恶心的倾斜的区间去掉。所以我们考虑新建一个以这两个力为轴的坐标系,再将每个点放进去。对于那两个力(x1,y1),(x2,y2),我们设它们分别为新坐标系中的x,y轴的单位向量。那么一个点(x,y)在新坐标系中的坐标(x’,y’)满足x(x1,y1)+y(x2,y2)=(x,y),我们用乘进去就会得到一条二元一次方程组,解出来就会得到(x’,y’),然后只要离散化一下,用树状数组求一个最长不下降子序列即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define db double
#define ll long long
using namespace std;
const int maxn=200005;
struct code{
    db x,y;
}b[maxn];
ll n,i,t,j,k,l,num;
ll f[maxn];
double a2,b2,x,y,xx,yy,x2,y2;
bool cmp(code x,code y){
    return x.x<y.x || x.x==y.x && x.y<y.y;
}
int lowbit(int x){
    return x&(-x);
}
ll find(int x){
    if (x<1) return 0;
    return max(f[x],find(x-lowbit(x)));
}
void insert(int x){
    if (x>num) return;
    f[x]=max(f[x],t);
    insert(x+lowbit(x));
}
db pan(db x,db y,db xx,db yy){
    if (!(xx<0 && yy<0 && x<0 && y<0)) xx=abs(xx),yy=abs(yy),x=abs(x),y=abs(y);
    return x*yy-y*xx;
}
int main(){
//  freopen("shield.in","r",stdin);freopen("shield.out","w",stdout);
    freopen("data.in","r",stdin);
    scanf("%lld%lf%lf%lf%lf",&n,&xx,&yy,&x2,&y2);
    //if (pan(xx,yy,x2,y2)<0) swap(xx,x2),swap(yy,y2);
    for (i=1;i<=n;i++){
        scanf("%lf%lf",&x,&y);
        b[i].x=(x*y2-x2*y)/(xx*y2-x2*yy);
        b[i].y=(x*yy-xx*y)/(x2*yy-xx*y2);
        swap(b[i].x,b[i].y);
    }   
    sort(b+1,b+n+1,cmp);
    for (i=1;i<=n;i++){
        if (b[i].x!=x) num++;x=b[i].x;
        b[i].x=num;
        swap(b[i].x,b[i].y);
    }
    sort(b+1,b+n+1,cmp);
    for (i=1;i<=n;i++){
        t=find(b[i].y)+1;
        insert(b[i].y);
    }
    t=find(num);
    printf("%lld\n",t);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值