• 2017.8.9的考试——脑抽的日常


    T1

    基因光线 light

    题目描述:

    黑大帅统治古古怪界后,一直在玩一种很奇葩的游戏。在一个二维平面上,他先复制了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
    

    提示:

    【样例说明】

           将c值改为0或-1可以击中4个小A,可以证明不可能同时击中5个小A。

    【数据范围】

           50%的数据满足a=0;

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

    时间限制:1000ms
    空间限制:256MByte

    这个题目是不是看起来可以AC,啊哈!我也是这么想的。然后就马不停蹄地写出了一下代码

    #include<bits/stdc++.h>
    using namespace std;
    int a,b,n,k,dp[1000005]={0},maxn=0;
    double d;
    struct nob{
    	int x,y;
    	double len;
    }s[100001];
    bool mmp(nob a,nob b){
    	return a.len<b.len;
    }
    int main(){
    	freopen("light.in","r",stdin);
    	freopen("light.out","w",stdout);
    	cin>>a>>b>>d>>n;
    	k=-1.0*a/b;
    	for (int i=1; i<=n; i++){
    		cin>>s[i].x>>s[i].y;
    		if (s[i].y>=s[i].x*k)
    		s[i].len=abs((a*s[i].x+b*s[i].y)*1.0/sqrt(a*a+b*b));
    		else s[i].len=-1.0*abs((a*s[i].x+b*s[i].y)*1.0/sqrt(a*a+b*b));
    		dp[i]=1;
    	}
    	sort(s+1,s+1+n,mmp);
    	for (int i=1; i<=n; i++){
    		for (int l=i-1; l>=1; l--){
    			if (s[i].len-s[l].len>d) break;
    			else dp[i]++;
    		}
    		for (int l=i+1; l<=n; l++){
    			if (s[l].len-s[i].len>d) break;
    			else dp[i]++;
    		}
    		maxn=max(maxn,dp[i]);
    		if (maxn==n){
    			cout<<maxn;
    			return 0;
    		}
    	}
    	cout<<maxn;
    	return 0;
    }//d=abs((ax0+by0+c)/sqrt(a*a+b*b));点到直线的距离公式
    

    然后就爆零了!呵呵呵呵呵。之后发现自己的一个思维错误,在上述程序中,我是以一个点为直线的中点,然后再以宽度d向上下两边延伸。但是!大部分时候!直线中点不在点上时能够包括更多的点!所以应该以点为直线的底边向上延伸2d距离。然后为了提高效率,在向上延伸的时候,可以用二分查找来缩短时间!但是你不用写二分就可以拿九十分!是不是很赚!以下是呆滞大佬的标程。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100005;
    double a,b,d,x,y;
    int n;
    struct dt{
    	double h;
    }e[maxn];
    bool cmp(dt p,dt q){
    	return p.h<q.h;
    }
    int main(){
    	freopen("light.in","r",stdin);
    	freopen("light.out","w",stdout);
    	int ans=0;
    	int head,tail,mid;
    	double t;
    	scanf("%lf%lf%lf%d",&a,&b,&d,&n);
    	t=sqrt(a*a+b*b);
    	for(int i=1;i<=n;i++){
    		scanf("%lf%lf",&x,&y);
    		e[i].h=(a*x+b*y)*1.0/t;
    	}
    	sort(e+1,e+1+n,cmp);
    	d=2*d;
    	for(int i=1;i<=n;i++){
    		head=i;tail=n;
    		while(head!=tail){
    			mid=(head+tail)>>1;
    			if(head==mid)
    				mid++;
    			if(e[mid].h<=(e[i].h+d))
    				head=mid;
    			else
    				tail=mid-1;
    		}
    		if((head-i+1)>ans) ans=head-i+1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    接下来是题解的解释。

    【基因光线】

           当c固定时,被击中的小A必满足|ax+by+c|/sqrt(a^2+b^2)<=d,即

    |ax+by+c|<=d*sqrt(a^2+b^2),令D=d*sqrt(a^2+b^2),则有|ax+by+c|<=D,则,

    -D<=ax+by+c<=D,即-D-c<=ax+by<=D-c,令R=ax+by,则-D-c<=R<=D-c,则问题转换为,在一维坐标轴上,小A的坐标为R,用2D长度最多覆盖几个小A。把小A按R从小到大排序,枚举作为开头的小A,用一个指针维护末尾的小A,不断更新答案,算法复杂度为O(nlogn),可以通过全部数据。

    T2

    好朋友 friend

    题目描述:

    noip2017就要来了,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
    

    提示:

    【样例解释】

           有两种选择。一种选择是<1,2>、<3,4>,最低亲密值为3;另一种选择是<1,4>、<2,3>,最低亲密值为1。所以最高的最低亲密值为3。

    【数据范围】

           50%的数据满足n<=20;

           80%的数据满足n<=1000;

           100%的数据满足n<=100000,-10^9<=w<=10^9

    时间限制:1000ms
    空间限制:256MByte

    这题题目以我现在的水平就只能水水分,因为。。。我根本就不知道什么是双联通分量。

    以下是题解。

    【好朋友】
    首先根据这道题的题意可以直接搜索求解。假如u未匹配,每次在u+1..n找一个未匹配的且与u有边相连的点,递归操作。中间可以加入最优化剪枝,可以通过零接表再加快速度,对于n<=60的数据都可以轻松通过。算法复杂度O(n!),可以通过50%的数据。
    仔细观察题目发现,这是一种特殊的图。众所周知n个点n-1条边的连通图是一棵树。
    假设原图是一棵树,任选一点作为根,那么叶子节点u必是与他的父亲father[u]相匹配,假如father[u]已被匹配,那么必然无解;否则father[u]标记为已匹配,继续递归,规模减2,这样我们就有了一个O(n)的贪心算法。
    n个点n条边的连通图就是在树上加一条边得到的图,有且仅有一个环。我们可以先找到这个环,环外的部分必然是树,我们可以通过上述贪心实现,然后再处理这个环就行了。
    假如环上已经有点被匹配了,那么环一定被断成了一段一段的链。链也是树的一种,同样可以贪心解决;假如环上所有的点都没有被匹配,这个环必然是含偶数个节点的环,直接考虑下列两种方案,取最优即可。

    找环有许多方法。O(n^2)的方法是直接暴力枚举每个点作为根,dfs一遍看是否有返祖边与根相连,找出所有环上的点。定一个环上的点作为开头,寻找下一个与他相连的环上的点,以此推出顺序。
    下面再介绍三种O(n)找出图的唯一环的方法:
    (1) 直接找出所有的双联通分量,其中大小不等于1的即是所求的环。然后再用上述方法求出顺序即可;
    (2) 任选一点作为根,建立一棵生成树,剩下的一条边即为环边。不妨设这条边为<u,v>那么v到lca(u,v)经过的点,u到lca(u,v)经过的点即为环上的点,并直接得到顺序。(lca(u,v)表示u,v的最近公共祖先)
    (3) 任选一点dfs,dfs过程中给边定向。假如某点在dfs时第2次经过,那么这个点必然是环上的点,一直退栈直到第一次经过这个点,中间的所有点均为环上的点,同时也得到了顺序。

    T3

    砍树 cut

    题目描述:

    小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
    

    提示:

    【样例说明】

           如下图,红色即为树的剩余部分。

    1502183002408952020.png

    【数据范围】

    数据组数

    n

    m

    q

    1-2

    1000

    500

    1000

    3

    50000

    20000

    1

    4

    50000

    1

    50000

    5-6

    50000

    30000

    50000

    7-10

    1000000

    500000

    500000

    时间限制:3000ms

    空间限制:256MByte

    离线做法?RMQ?单调队列?水吧……水吧……以下是题解。

    【砍树】
    首先,这道题目可以分成两部分:砍树和询问的部分。
    对于砍树的部分,我们可以暴力做,这样的复杂度是O(nm)的,能通过20%的数据。
    观察直线的解析式我们发现,对于同个k、同个x,kx恒定不变,所以影响某棵树高度的直线只能是经过这棵树的直线中b值最小的那条。这样我们可以把三种砍树方式分开做,然后按b值从小到大排序,然后用染色的方法做。这里可以用线段树或并查集来染色,总效率O(mlogm+n),可以通过60%的数据。
    题目中有一个重要的条件就是这些砍树区间互不包含,这样我们就可以使用单调队列的方法。按左端点计数排序,扫描,维护一个区间右端点递增,b值递增的单调队列。由于每个点出入队列最多一次,所以总效率为O(n+m)可以通过100%的数据。
    经过前面一部分,其实我们已经知道了每棵树的最终高度。
    对于询问的部分,我们同样可以暴力做,复杂度为O(nq),可以通过20%的数据。
    这其实是一个经典的区间最小值的问题,可以用线段树或RMQ在线解决,总效率O(qlogn+n)或O(nlogn+q),可以通过60%的数据。
    这道题目并没有修改操作,只是单纯的询问,所以我们考虑离线做法。将询问区间按右端点计数排序。扫描n棵树的过程中维护一个位置递增,h值递减的单调栈。当经过的点是某个询问中的右端点时,二分左端点位置,得到最小值,总效率为O(n+qlogn),但对于随机数据,单调栈是很小的,所以可以通过80%的数据。
    考虑到现在算法的瓶颈在于二分,我们可以利用并查集,动态维护每个点往右最近的且在单调栈中的点,总效率O(n+q),可以通过100%的数据。
    下面是对于数据[9,8,9,1]的做法:

    1. 指针:0
    栈:(空)
    2. 指针:1
    栈:1(9)
    3. 指针:2
    1退栈,2进栈,1连2;
    栈:2(8)
    4. 指针:3
    栈:2(8),3(9)
    5. 指针:1
    3退栈,3连4,2退栈,2连4;
    栈:4(1)

    呵呵呵呵,第一题翻车还真的是意外。还是要小心啊。。。

    made by cain-

  • 相关阅读:
    基于SAR对Linux资源的监控shell脚本
    Python3+RobotFramewok 用户自定义库的开发(四)
    Python3+RobotFramewok 循环判断以及Evaluate用法(三)
    Python3+RobotFramewok 快速入门(二)
    Python3+RobotFramewok RIDE环境搭建(一)
    MySQL主从双向同步
    笔记:网络协议
    Jmeter组成结构及运行原理
    Selenium WebDriver的实现及工作原理
    Jenkins+maven环境部署
  • 原文地址:https://www.cnblogs.com/cain-/p/7327396.html
Copyright © 2020-2023  润新知