三分

18 篇文章 0 订阅
1 篇文章 0 订阅

题:

bzoj3330
bzoj3203YES
bzoj3533YES
bzoj3874
bzoj1857YES
bzoj1229YES

bzoj3533☆

题意:

维护一个向量集合,在线支持以下操作:
"Axy(|x||y|108)" :加入向量(x,y);
"Qxylr(|x||y|1081LRT ,其中T为已经加入的向量个数)询问第L个到第R个加入的向量与向量(x,y)的点积的最大值。
集合初始时为空。

分析:

设询问点为 (x,y) ,对应答案最大的点为 (x0,y0)
首先 (x0,y0) 一定在区间 [l,r] 区间中所有点的凸包上。且
y0 (x0,y0) 在上凸壳上
y<0 (x0,y0) 在下凸壳上
那么用线段树维护一下区间,但假如每次插入都更新凸包的话时间就爆炸了。发现如果区间右端点还没插入时这整段区间是不会查询的。于是乎只有插入区间右端点是才更新整个区间的上下凸壳。嗯~时间复杂度就是 O(nlog2n) 了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>

using namespace std;
typedef long long LL;
const LL inf=21333333333333333;
const int N=1048576;
int n,m,x,y,l,r,v[N],size;
long long lastans,ans;
struct node{int x,y,l,r,op;}g[N];
char s[N],tt[N];
struct point{
    LL x,y;
    point(){}
    point(int _x,int _y):x(_x),y(_y){}
    friend point operator - (point a,point b){
        return point(a.x-b.x,a.y-b.y);
    }
    friend point operator + (point a,point b){
        return point(a.x+b.x,a.y+b.y);
    }
    friend LL operator * (point a,point b){
        return a.x*b.y-a.y*b.x;
    }
    friend LL operator ^ (point a,point b){
        return a.x*b.x+a.y*b.y;
    }
    friend bool operator < (point a,point b){
        if (a.x==b.x)
            return a.y<b.y;
        return a.x<b.x;
    }
}p[N],t[N];

struct range{
    point *up,*dw;
    int ut,dt;
    void gao(int l,int r){
        up=new point[r-l+2];
        dw=new point[r-l+2];
        int top=0;ut=dt=0;
        for (int i=l;i<=r;i++)
            t[++top]=p[i];
        sort(t+1,t+top+1);
        for (int i=1;i<=top;i++){
            while (ut>1 && (t[i]-up[ut])*(up[ut]-up[ut-1])<=0) ut--;
            up[++ut]=t[i];
            while (dt>1 && (dw[dt]-dw[dt-1])*(t[i]-dw[dt])<=0) dt--;
            dw[++dt]=t[i];
        }
    }
    LL query(point k){
        LL res=-inf;
        if (k.y>=0){
            int l=1,r=ut;
            while (r-l>2){
                int ll=l+(r-l)/3;
                int rr=r-(r-l)/3;
                if ((up[ll]^k)<(up[rr]^k))
                    l=ll; else r=rr;
            }
            for (int i=l;i<=r;i++)
                res=max(res,up[i]^k);
        } else {
            int l=1,r=dt;
            while (r-l>2){
                int ll=l+(r-l)/3;
                int rr=r-(r-l)/3;
                if ((dw[ll]^k)<(dw[rr]^k))
                    l=ll; else r=rr;
            }
            for (int i=l;i<=r;i++)
                res=max(res,dw[i]^k);
        }
        return res;
    }
}tree[N];

LL query(int rt,int l,int r,int ll,int rr,point k){
    if (ll<=l && r<=rr){
        if (!v[rt]) tree[rt].gao(l,r);
        v[rt]=1;return tree[rt].query(k);
    }
    int mid=(l+r)>>1;LL res=-inf;
    if (ll<=mid) res=max(res,query(2*rt,l,mid,ll,rr,k));
    if (rr> mid) res=max(res,query(2*rt+1,mid+1,r,ll,rr,k));
    return res;
}

int read(){  
    int res=0,f=1;char c=getchar();  
    while(!isdigit(c))f=f==-1||c=='-'?-1:1,c=getchar();  
    while(isdigit(c))res=res*10+c-'0',c=getchar();  
    return (res*f);  
} 

int gao(){return lastans&0x7fffffff;}
int main(){
    freopen("a.in","r",stdin);
    scanf("%d%s",&m,s);
    for (int i=1;i<=m;i++){
        scanf("%s",tt);
        if (tt[0]=='A'){
            x=read();y=read();
            g[i].x=x;g[i].y=y;
            g[i].op=1;n++;
        } else {
            x=read();y=read();
            l=read();r=read();
            g[i].x=x;g[i].y=y;
            g[i].l=l;g[i].r=r;
            g[i].op=2;
        }
    }
    for (int i=1;i<=m;i++){
        if (g[i].op==1){
            x=g[i].x;y=g[i].y;
            if (s[0]!='E'){
                x^=gao();y^=gao();
            }
            p[++size]=point(x,y);
        } else {
            x=g[i].x;y=g[i].y;
            l=g[i].l;r=g[i].r;
            if (s[0]!='E'){
                x^=gao();y^=gao();
                l^=gao();r^=gao();
            }
            lastans=query(1,1,n,l,r,point(x,y));
            printf("%lld\n",lastans);
        }
    }
}

bzoj1857

三分套三分~就是分别三分两线段。
证明:http://blog.csdn.net/geotcbrl/article/details/48833339

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;
double p,q,len,ans;
const double eps=0.00000001;
struct node{
    double x,y;
    node(){}
    node(double _x,double _y):x(_x),y(_y){}
    friend double dis(node a,node b){
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    friend node operator - (node a,node b){
        return node(a.x-b.x,a.y-b.y);
    }
    friend node operator + (node a,node b){
        return node(a.x+b.x,a.y+b.y);
    }
    friend node operator / (node a,double x){
        return node(a.x/x,a.y/x);
    }
}a,b,c,d;

double find(node a){
    node l=c,r=d;
    double res=min(dis(a,r)/len,(dis(a,l)+dis(l,r))/len);
    while (dis(l,r)>eps){
        node ll=l+(r-l)/3.0;
        node rr=r-(r-l)/3.0;
        double t1=dis(d,ll)/q,t2=dis(d,rr)/q;
        t1+=dis(ll,a)/len,t2+=dis(rr,a)/len;
        if (t1<t2) 
            res=min(res,t1),r=rr; else
            res=min(res,t2),l=ll;
    }
    return res;
}

int main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%lf%lf%lf%lf",&a.x,&a.y,&b.x,&b.y);
    scanf("%lf%lf%lf%lf",&c.x,&c.y,&d.x,&d.y);
    scanf("%lf%lf%lf",&p,&q,&len);
    node l=a,r=b;ans=min(find(a),dis(l,r)/p+find(b));
    while (dis(l,r)>eps){
        node ll=l+(r-l)/3.0;
        node rr=r-(r-l)/3.0;
        double t1=dis(a,ll)/p,t2=dis(a,rr)/p;
        double r1=find(ll),r2=find(rr);
        r1+=t1;r2+=t2;
        if (r1<r2)
            ans=min(ans,r1),r=rr; else
            ans=min(ans,r2),l=ll;
    }
    printf("%.2lf\n",ans);
}

bzoj1229

若天数少的话就可以用费用流水掉了。
不然就贪心乱搞。
显然能去慢洗肯定比去快洗好(快洗比慢洗贵)。
现在问题来了,我们究竟要多少玩具捏。三分一波一开始的玩具个数(不会证明)然后在贪心乱搞一下就好了。

int gao(int x){
    sl.clear();qk.clear();tm.clear();
    int res=x*(tc-c2);
    sl.push_back(node(0,x));
    for (int i=1;i<=d;i++){
        while (!tm.empty() && i-tm.front().x>=n1)
            qk.push_back(tm.front()),tm.pop_front();
        while (!qk.empty() && i-qk.front().x>=n2)
            sl.push_back(qk.front()),qk.pop_front();
        int T=t[i];
        while (T){
            if (!sl.empty()){
                if (sl.back().c>=T){
                    sl.back().c-=T;
                    res+=T*c2;T=0;
                } else {
                    T-=sl.back().c;
                    res+=sl.back().c*c2;
                    sl.pop_back();
                }
            } else
            if (!qk.empty()){
                if (qk.back().c>=T){
                    qk.back().c-=T;
                    res+=T*c1;T=0;
                } else {
                    T-=qk.back().c;
                    res+=qk.back().c*c1;
                    qk.pop_back();
                }
            } else return inf;
        }
        tm.push_back(node(i,t[i]));
    }
    return res;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值