20190729

A.

黑大帅统治古古怪界后,一直在玩一种很奇葩的游戏。在一个二维平面上,他先复制了n个小A,把他们放在不同的位置,然后射出一条ax+by+c=0的基因光线,宽度为d,即离这条直线的距离不大于d的小A会被射中。当然,某些悲剧的小A就会被射中,并变成黑小A。当然,这不是重点。玩了很久后,黑大帅猛然发现,自己竟然一次都没有射中小A。黑大帅怒了,于是他开启了作弊模式,将c改成自己想要的任意数值。现在,黑大帅想知道,在开启了作弊模式后,他射出一道基因光线最多能击中几个小A。

输入格式

第一行五个数字a,b,d,n,接下来n行每行两个数字x,y表示这个小A的坐标。

输出格式

一行一个数字表示最多能击中几个小A。

样例输入

1 -1 0.707106782 5
0 0
1 0
0 1
2 0
2 1

样例输出

4

数据范围

50%的数据满足a=0;
100%的数据满足n<=100000,其余所有数值均为绝对值不大于1000的实数。

考虑 \(a=0\) 的情况。
直接按y坐标排序然后two-pointers。
然后就能想到用点到直线的距离公式把点排序。
公式:有一点 \(P(x_0,y_0)\) ,直线 \(Ax+By+C=0\) ,则 \[d=\frac{Ax_0+By_0+C}{\sqrt{A^2+B^2}}\]
\(P\) 在直线的不同侧时, \(d\) 的符号也不同。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=100003;
const double eps=1e-8;
int sgn(double x){return x<-eps?-1:x>eps;}
struct point{
    double x,y;
}pnt[maxn];
int n,ans;
double a,b,d,sq;
double dis(point p){
    return a*p.x+b*p.y;
}
int main(){
    scanf("%lf%lf%lf%d",&a,&b,&d,&n);
    sq=sqrt(a*a+b*b);
    for(int i=1;i<=n;i++)scanf("%lf%lf",&pnt[i].x,&pnt[i].y);
    sort(pnt+1,pnt+n+1,[](point p,point q){return sgn(dis(p)-dis(q))<0;});
    for(int i=1,j=1;i<=n;i++){
        for(;j<=n&&sgn((dis(pnt[j])-dis(pnt[i]))/sq-d*2)<=0;j++);
        ans=max(ans,j-i);
    }
    printf("%d\n",ans);
    return 0;
}

B.

noip2011就要来了,W校的同学们不仅看重这次比赛,更看重noip2011和谁住在同一个房间。同学之间的关系好坏可以用一个亲密值表示,亲密值越大,两个同学关系越好。小A作为W校信息组的组长,自然想要让同学们在比赛前能好好休息,放松心情,让同学们在赛场上能够超常发挥。他现在知道自己预订的房间都是双人间,且知道这n个同学之间的关系。n个同学的关系可以用一个n条双向边连通图来描述,即某个同学只愿意和与他有边相连的同学住同一个房间,边权即为两个同学的亲密值。数据保证没有重边、自环。现在小A想知道在让所有同学的要求满足的情况下,亲密值最低的一对同学亲密值最高是多少。

输入格式

第一行一个正整数n,下面n行每行三个数u,v,w,表示u到v有一条边权为w的双向边。

输出格式

假如无论如何都无法满足所有同学的要求,输出”no answer”,否则输出亲密值最低的一对同学的最高亲密值。

样例输入

4
1 2 3
2 3 10
3 4 3
1 4 1

样例输出

3

数据范围

50%的数据满足n<=20;
80%的数据满足n<=1000;
100%的数据满足n<=100000,-10^9<=w<=10^9

首先这是一棵基环树,就不关二分图和网络流什么事了。
乍一看像是二分答案然后跑dp。
实际上观察发现,为保证每一个节点都能被另一个节点匹配,我们建一个队列,从叶子开始,遍历到每一个度数为1的节点,删除它和它父亲,并把新产生的度为1的节点推入队列,以此类推,最后要么所有节点都匹配成功,要么还剩下一个环。如果是环就只会存在两种方案,dfs把环拉出来统计即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long D;
const int maxn=100003;
const D INF=0x3f3f3f3f3f3f3f3fll;
struct edge{
    int v;
    D w;
    bool vis;
    edge(){}
    edge(int _v,D _w,bool _vis=0):v(_v),w(_w),vis(_vis){}
};
vector<edge> g[maxn];
int n,cnt,deg[maxn],del[maxn];
D a[maxn];
void init(int u,int last){
    del[u]=3;
    bool flag=1;
    for(int i=0;i<int(g[u].size());i++){
        int v=g[u][i].v;
        if(v==last)continue;
        if(del[v]){
            if(flag)a[0]=g[u][i].w,flag=1;
            continue;
        }
        a[++cnt]=g[u][i].w;
        init(v,u);
    }
}
D work(){
    D ret=INF;
    queue<int> q;
    for(int i=1;i<=n;i++){
        if(deg[i]==1){
            q.push(i);
        }
    }
    while(!q.empty()){
        int x=q.front(),y=0;
        q.pop();
        if(del[x])continue;
        del[x]=1;
        for(int i=0;i<int(g[x].size());i++){
            int v=g[x][i].v;
            if(!del[v]){
                y=v;
                ret=min(ret,g[x][i].w);
                break;
            }
        }
        if(y==0){puts("no answer");exit(0);}
        del[y]=2;
        for(int i=0;i<int(g[y].size());i++){
            int v=g[y][i].v;
            if(!del[v]){
                deg[v]--;
                if(deg[v]==1){
                    q.push(v);
                }
            }
        }
    }
    return ret;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(edge(v,w)),g[v].push_back(edge(u,w));
        deg[u]++,deg[v]++;
    }
    D ans=work();
    int rt=0;
    for(int i=1;i<=n;i++)if(!del[i]){rt=i;break;}
    if(rt==0){printf("%lld\n",ans);return 0;}
    init(rt,0);
    D ans1=ans,ans2=ans;
    for(int i=0;i<=cnt;i+=2)ans1=min(ans1,a[i]);
    for(int i=1;i<=cnt;i+=2)ans2=min(ans2,a[i]);
    ans=max(ans1,ans2);
    printf("%lld\n",ans);
    return 0;
}

C.

小A是小B家的园丁。小B的家里有n棵树,第i棵树的横坐标为i。一天,小B交给小A一个任务,让他降低自己家中的某些树木的高度。这个任务对小A来说十分简单,因为他有一把极其锋利的斧头和一门独门砍树秘籍,能够轻易地砍断任何参天大树。小A的砍树方法有3种,都是沿着一条y=kx+b的直线砍一段区间的树,相同的方法k值相同。只用了一个下午,小A就完成了小B的任务。第二天,小B来视察小A的任务完成情况。小B想知道小A是否真的用心砍树,于是提出了q个询问,每次询问一段区间中最低的树的高度。小A当然是不会记住树木的砍伐情况的,他只知道自己按什么顺序,使用了什么方法,砍了哪个连续区间的树,而且区间都是互不包含的。现在小A想请你帮帮他,回答小B的询问。

输入格式

第一行三个整数k1,k2,k3表示小A三种砍树方法的斜率值;
第二行一个数n,表示一共有n棵树;
第三行n个数hi,分别表示n棵树的高度;
第四行一个数m,表示小A一共进行了m次操作;
接下来m行,每行四个数L,R,p,b,表示用第p种方法,即用y=kp+b的直线砍[L,R]区间的树;
接下来一行一个数q,表示小B的询问数;
接下来q行,每行两个数L,R,表示询问[L,R]区间中最低的树的高度。

输出格式

一共q行,每行一个数h表示对应的回答。

样例输入

1 0 -1
4
10 30 20 1
2
3 4 2 5
1 3 3 10
2
1 2
2 3

样例输出

8
5

数据范围

n<=1000000,m<=500000

空间限制 64MB

提示

如下图,红色即为树的剩余部分。
1516397-20190730082144143-1039378367.jpg

注意到本题使用离线算法,而且直线的斜率只有三种。
所以先按左端点排序,然后直接用三个线段树或单调队列维护。
最后查询时使用线段树。(不能用ST表,否则MLE)

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long D;
const int maxn=1000003;
struct QQ{
    int l,r,p;
    D b;
    bool operator <(const QQ &x)const{return l<x.l;}
}q[maxn];
int n,Q;
deque<QQ> deq[4];
D a[maxn],k[4],t[maxn<<2];
void build(int p,int l,int r){
    if(l==r){
        t[p]=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p]=min(t[p<<1],t[p<<1|1]);
}
D query(int p,int l,int r,int seg_l,int seg_r){
    if(seg_l<=l&&r<=seg_r)return t[p];
    int mid=(l+r)>>1;
    if(seg_l<=mid&&seg_r>mid)return min(query(p<<1,l,mid,seg_l,seg_r),query(p<<1|1,mid+1,r,seg_l,seg_r));
    else if(seg_l<=mid)return query(p<<1,l,mid,seg_l,seg_r);
    else return query(p<<1|1,mid+1,r,seg_l,seg_r);
}
int main(){
    scanf("%lld%lld%lld%d",k+1,k+2,k+3,&n);
    for(int i=1;i<=n;i++)scanf("%lld",a+i);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++){
        scanf("%d%d%d%lld",&q[i].l,&q[i].r,&q[i].p,&q[i].b);
        q[i].r++;
    }
    sort(q+1,q+Q+1);
    for(int i=1,j=1;i<=n;i++){
        for(;j<=Q&&q[j].l<=i;j++){
            QQ qq=q[j];
            deque<QQ> &que=deq[qq.p];
            while(!que.empty()&&qq.b<=que.back().b)que.pop_back();
            que.push_back(qq);
        }
        for(int j=1;j<=3;j++){
            deque<QQ> &que=deq[j];
            while(!que.empty()&&que.front().r<=i)que.pop_front();
            if(!que.empty()){
                QQ res=que.front();
                a[i]=min(a[i],k[res.p]*i+res.b);
            }
        }
    }
    build(1,1,n);
    scanf("%d",&Q);
    while(Q--){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%lld\n",query(1,1,n,x,y));
    }
    return 0;
}

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/11267683.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值