2022-7-25 考试

小凸想跑步

题目叙述

给定一个凸多边形,在这个凸多边形内部随机一个点,使得它和前两个顶点形成的边形成的三角形是和其他边形成三角形面积最小的。求概率。

题解

这个点满足的限制其实很简单,就是在凸多边形内部并且满足形成的三角形比其他的都小。
每个边组成的三角形对于这个点来说就是一个新的限制。
这样就得到了 \(2n\) 个限制,做一个半平面交就可以了。

总结

  • 不能太往找性质方向思考。有时候应该思考普遍的做法。

代码

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const double eps=1e-8;
bool same(double x,double y){return (x-y<=eps&&y-x<=eps)?1:0;}
struct dot{
	double x,y;
	dot():x(0),y(0){}
	dot(double _x,double _y):x(_x),y(_y){}
};
dot operator+(const dot &A,const dot &B){return dot(A.x+B.x,A.y+B.y);}
dot operator-(const dot &A,const dot &B){return dot(A.x-B.x,A.y-B.y);}
dot operator*(const dot &A,const double &t){return dot(A.x*t,A.y*t);}
double _cross(const dot &A,const dot &B){return A.x*B.y-B.x*A.y;}
struct line{
	dot g,k;
	double arc;
	line(dot _g,dot _k):g(_g.x,_g.y),k(_k.x,_k.y){arc=atan2(k.y,k.x);}
	line():g(0,0),k(0,0){}
};
bool right(dot x,line l){return _cross(x-l.g,l.k)>=0;}
dot _cross(const line &A,const line &B){
	double tmp=_cross(A.g-B.g,B.k)/_cross(B.k,A.k);
	return A.g+(A.k*tmp);
}
const int MN=2e5+5;
int main(){
	static int N,totlim;
	static dot A[MN];
	static line lim[MN];
	scanf("%d",&N);
	double s1=0;
	FOR(i,0,N-1)scanf("%lf%lf",&A[i].x,&A[i].y);
	FOR(i,0,N-1)s1+=_cross(A[i],A[(i+1)%N]);
	FOR(i,0,N-1)lim[++totlim]=line(A[i],A[(i+1)%N]-A[i]);
	FOR(i,1,N-1){
		int nxt=(i+1)%N;
		double p=A[0].y-A[1].y-A[i].y+A[nxt].y;
		double q=A[1].x-A[0].x-A[nxt].x+A[i].x;
		double r=A[0].x*A[1].y-A[1].x*A[0].y+A[nxt].x*A[i].y-A[i].x*A[nxt].y;
		dot u(0,0),v(-q,p);
		if(same(q,0))u=dot(-r/p,0);
		else u=dot(0,-r/q);
		lim[++totlim]=line(u,v);
	}
	sort(lim+1,lim+totlim+1,[](const line &x,const line &y){
		if(!same(x.arc,y.arc))return x.arc<y.arc;
		else return right(y.g,x);
	});
	static int head,tail;
	static dot cro[MN];
	static line q[MN];
	head=tail=1;
	q[1]=lim[1];
	FOR(i,2,totlim){
		if(same(lim[i].arc,lim[i-1].arc))continue;
		while(head<tail&&right(cro[tail-1],lim[i]))--tail;
		while(head<tail&&right(cro[head],lim[i]))++head;
		q[++tail]=lim[i];
		if(head<tail)cro[tail-1]=_cross(q[tail-1],q[tail]);
	}
	while(head<tail&&right(cro[tail-1],q[head]))--tail;
	while(head<tail&&right(cro[head],q[tail]))++head;
	cro[tail]=_cross(q[head],q[tail]);
	double s2=0;
	FOR(i,head+1,tail-1)s2+=_cross(cro[i]-cro[head],cro[i+1]-cro[head]);
	printf("%.4lf\n",s2/s1);
	return 0;
}

匹配

题目叙述

给定一个二分图,两边点都有权值,求方案数满足二分图的子集 \(s\) 权值总和 \(\ge t\) 并且存在一个匹配包含 \(s\)
\(n\le 20\)

题解

首先要有自信。没有思考出来什么比较正常的做法基础上,一定是有一些神秘结论,答案往往就在那个方向稍微思考了一下但是觉得不可能就退缩了的地方。
结论是如果左部点和右部点分别都存在一个匹配包含他,那么就一定存在一个匹配包含左和右。
证明也很简单,对于任意两个左部点和右部点的集合 \(s\)\(t\) ,取集合 \(M_1\)\(M_2\) 满足 \(M_1\) 包含 \(s\) 并且 \(M_2\) 包含 \(t\) 。并且 \(M_1\) 中任意一条边的两个端点都和 \(s\) 有交集,并且 \(M_2\) 中任意一条边的两个端点都和 \(t\) 有交集。
考虑 \(M_1\)\(M_2\) 的并集,他们一定是若干条链和若干个环。
环一定长度是偶数,所以可以两两分组。链长度为偶数的也不需要讨论了,只需要讨论长度为奇数的了。
如果长度为奇数的链两个端点都在 \(s\cup t\) 里面(注意这条链是极长的),那么这条链整个一定都在 \(s\cup t\) 里面。
这是因为左部属于 \(s\) 的点匹配到右边,必须匹配到 \(t\) 中,如果不在 \(t\) 中,首先下一条匹配到左边的点一定是为了覆盖右边的点,就是右边到左边的匹配,所以必然下一个点就在右边。所以每个点都在 \(s\cup t\) 之中。
考虑这条链的链尾,一定是 \(s\) 中的,而如果他想存在,就必须走向右边。就是说找到他在 \(M_1\) 中的匹配点的意思。

总结

  • 对自己见过的套路数量要自信,然后思考有没有什么结论。没有套路能套的话就得思考有没有什么结论。有这样的结论就是要继续向下想的。

区间

题目叙述

给定一个长度为 \(n\) 的排列,求对于一个区间 \([l,r]\) ,最小的包含他的区间满足这个区间值域是连续的。多次询问。

题解

考虑这样一种暴力,对于这个区间中没有的数,就都给他找出来,然后将这个区间扩大为包含这些数的最左和最右的那个区间。
这样不断操作,直到到达一种状态满足区间是连续的,那么这样得到的一定是最小的区间。
到这里已经可以使用CF1707E 的解法解决掉了。
但是不够好。
考虑这个过程导出了一件事情就是所有包含这个区间并且形成连续段的区间,都一定包含最小的区间。因为最小的区间是不包含不可的。
因此,只要找到最小的右端点满足存在一个左端点和这个右端点形成的区间包含这个区间,这个右端点一定就是包含 \([l,r]\) 最小的右端点。
左端点也是这样的。
所以首先按照那个经典的维护区间最小值个数的题目,改为线段树上二分,求出右端点对应的最小左端点和左端点对应的最大右端点,再写个 st 表维护这件事情就可以了。

题解

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
typedef long long LL;
const int MN=1e5+5;
namespace ST{
	const int MND=MN*4;
	int minv[MND],tag[MND];
	void init(){memset(minv,0,sizeof(minv)),memset(tag,0,sizeof(tag));}
	void do_opr(int o,int v){
		minv[o]+=v;
		tag[o]+=v;
	}
	void spread(int o){
		if(tag[o]){
			do_opr(o<<1,tag[o]);
			do_opr(o<<1|1,tag[o]);
			tag[o]=0;
		}
	}
	#define lc ((o)<<1)
	#define rc ((o)<<1|1)
	void update(int o){minv[o]=min(minv[lc],minv[rc]);}
	void add(int o,int l,int r,int ql,int qr,int v){
		if(l>qr||r<ql)return;
		if(ql<=l&&r<=qr)return do_opr(o,v);
		int mid=(l+r)>>1;
		spread(o);
		add(o<<1,l,mid,ql,qr,v);
		add(o<<1|1,mid+1,r,ql,qr,v);
		update(o);
	}
	int find(int o,int l,int r,int ri){
		if(l>ri)return -1;
		if(minv[o]!=0)return -1;
		if(l==r){
			if(minv[o]==0)return l;
			else return -1;
		}
		spread(o);
		int mid=(l+r)>>1,tmp=find(lc,l,mid,ri);
		if(tmp!=-1)return tmp;
		else return find(rc,mid+1,r,ri);
	}
	void print(int o,int l,int r){
		if(l==r){
			cerr<<minv[o]<<" ";
			return;
		}
		int mid=(l+r)>>1;
		spread(o);
		print(lc,l,mid);
		print(rc,mid+1,r);
	}
	#undef lc
	#undef rc
};
int N;
void calc(int *A,int *left){
	static int smax[MN],smin[MN],maxtop,mintop;
	maxtop=mintop=0;
	ST::init();
	ST::add(1,1,N,1,N,1);
	FOR(i,1,N){
		while(maxtop&&A[smax[maxtop]]<A[i]){
			ST::add(1,1,N,smax[maxtop-1]+1,smax[maxtop],-A[smax[maxtop]]);
			// cerr<<smax[maxtop]<<" "<<las<<" "<<-A[smax[maxtop]]<<endl;
			--maxtop;
		}
		ST::add(1,1,N,smax[maxtop]+1,i,A[i]);
		// cerr<<smax[maxtop]+1<<" "<<i<<" "<<A[i]<<endl;
		smax[++maxtop]=i;
		while(mintop&&A[smin[mintop]]>A[i]){
			ST::add(1,1,N,smin[mintop-1]+1,smin[mintop],A[smin[mintop]]);
			// cerr<<smin[mintop]<<" "<<las<<" "<<A[smin[mintop]]<<endl;
			--mintop;
		}
		ST::add(1,1,N,smin[mintop]+1,i,-A[i]);
		// FOR(j,1,mintop)cerr<<smin[j]<<" ";
		// cerr<<endl;
		// ST::print(1,1,N);
		// cerr<<endl;
		// cerr<<smin[mintop]+1<<" "<<i<<" "<<-A[i]<<endl;
		smin[++mintop]=i; ////////////
		ST::add(1,1,N,1,i,-1);
		left[i]=ST::find(1,1,N,i);
		// ST::print(1,1,N);
		// cerr<<endl<<"______________________________"<<endl;
	}
	// 开始维护反了!!!
	// FOR(i,1,N)cerr<<left[i]<<" ";
	// cerr<<endl;
}
int A[MN],ml[MN],mr[MN];
int st_min[MN][20],st_max[MN][20],lg[MN];
int main(){
	freopen("interval.in","r",stdin);
	freopen("interval.out","w",stdout);
	scanf("%d",&N);
	FOR(i,1,N)scanf("%d",&A[i]);
	calc(A,ml);
	reverse(A+1,A+N+1);
	calc(A,mr);
	reverse(mr+1,mr+N+1);
	FOR(i,1,N)mr[i]=N-mr[i]+1;
	// FOR(i,1,N)cerr<<ml[i]<<" ";
	// cerr<<endl;
	// FOR(i,1,N)cerr<<mr[i]<<" ";
	// cerr<<endl;
	FOR(i,1,N)st_min[i][0]=ml[i],st_max[i][0]=mr[i];
	FOR(i,1,16)FOR(j,1,N-(1<<i)+1){
		st_min[j][i]=min(st_min[j][i-1],st_min[j+(1<<(i-1))][i-1]);
		st_max[j][i]=max(st_max[j][i-1],st_max[j+(1<<(i-1))][i-1]);
	}
	FOR(i,2,N)lg[i]=lg[i>>1]+1;
	int Q=0;scanf("%d",&Q);
	while(Q--){
		int l=0,r=0;scanf("%d%d",&l,&r);
		int ansl=0,ansr=0;

		int p=r,lev=lg[N-r+1];
		ROF(i,lev,0)if(p+(1<<i)-1<=N&&st_min[p][i]>l)p+=(1<<i);
		ansr=p;

		p=l,lev=lg[l];
		ROF(i,lev,0)if(p-(1<<i)+1>=1&&st_max[p-(1<<i)+1][i]<r)p-=(1<<i);
		ansl=p;

		printf("%d %d\n",ansl,ansr);
	}
	// system("grep VmPeak /proc/$PPID/status >/dev/tty");
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2022-07-25 21:56  YouthRhythm  阅读(37)  评论(0)    收藏  举报