省选测试38

总结

暴力专场, \(T1\) 写了一个 \(n^42^n\) 的做法本地跑 \(1.6s\),交上去 \(n=15\) 的全 \(T\)了,剩下的两道题和预期的一样。

A. 选拔赛

分析

把前 \(k\) 个人称为 \(A\) 组,把后 \(n-k\) 个人称为 \(B\) 组。

那么方案希望 \(A\) 组中的最低分大于等于 \(B\) 组中的最高分。

去枚举 \(A\) 组中的最低分 \(x\),这个最低分最多只有 \(n^2\) 种情况。

实现一个函数 \(f(l,r)\) 表示 \(A\) 组中的分数都大于等于 \(l\)\(B\) 组中的分数都小于等于 \(r\) 的方案。

为了去重,需要强制 \(A\) 组中有一个人是最低分。

那么答案就是 \(\sum_{x}f(x,x)-f(x+1,x)\)

对于 \(c\) 数组按照权值从小到大排序。

\(cnt1[i]\) 表示 \(c[i]\) 能够匹配 \(A\) 中的 \(cnt1[i]\) 个元素,\(cnt2[i]\) 表示 \(c[i]\) 能够匹配 \(B\) 中的 \(cnt2[i]\) 个元素。

随着 \(c\) 的增大,\(cnt1\) 会增大,\(cnt2\) 会减小。

也就是说 \(c[i]\)\(A\) 组中能够匹配上的,\(c[i+1]\) 一定能够匹配上, \(c[i]\)\(B\) 组中能够匹配上的,\(c[i-1]\) 一定能够匹配上。

所以对于 \(A\) 组的方案,在 \(c\) 数组中正序考虑,对于 \(B\) 组的方案,在 \(c\) 数组中倒序考虑。

因为之前匹配的 \(A\) 在之后不能匹配,所以 \(c[x]\)\(i\) 个匹配上 \(A\) 的方案为 \(cnt1[x]-(i-1)\)\(B\) 数组同理。

实现的时候设 \(dp[i][j]\) 表示 \(c\) 数组中的前 \(i\) 个数已经有 \(j\) 个匹配到了 \(A\) 数组中的方案数,最终的答案就是 \(dp[n][k]\)

时间复杂度 \(n^4\)

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define rg register
template<typename T>void read(rg T &x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=105,mod=1e9+7;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
int n,k,a[maxn],b[maxn],ans,f[maxn][maxn],sta[maxn*maxn],tp,cnt1[maxn],cnt2[maxn];
int getf(rg int l,rg int r){
	memset(cnt1,0,sizeof(cnt1));
	memset(cnt2,0,sizeof(cnt2));
	memset(f,0,sizeof(f));
	for(rg int i=1;i<=k;i++){
		for(rg int j=1;j<=n;j++){
			if(a[i]+b[j]>=l) cnt1[j]++;
		}
	}
	for(rg int i=k+1;i<=n;i++){
		for(rg int j=1;j<=n;j++){
			if(a[i]+b[j]<=r) cnt2[j]++;
		}
	}
	f[0][0]=1;
	for(rg int i=0;i<n;i++){
		for(rg int j=0;j<=i && j<=k;j++){
			if(cnt1[i+1]>j) f[i+1][j+1]=addmod(f[i+1][j+1],mulmod(f[i][j],(cnt1[i+1]-j)));
			if(cnt2[i+1]>n-k-(i+1-j)) f[i+1][j]=addmod(f[i+1][j],mulmod(f[i][j],(cnt2[i+1]-(n-k-(i+1-j)))));
		}
	}
	return f[n][k];
}
int main(){
	read(n),read(k);
	for(rg int i=1;i<=n;i++) read(a[i]);
	for(rg int i=1;i<=n;i++) read(b[i]);
	std::sort(a+1,a+n+1);
	std::reverse(a+1,a+n+1);
	std::sort(b+1,b+n+1);
	for(rg int i=1;i<=n;i++){
		for(rg int j=1;j<=n;j++){
			sta[++tp]=a[i]+b[j];
		}
	}
	std::sort(sta+1,sta+tp+1);
	tp=std::unique(sta+1,sta+tp+1)-sta-1;
	for(rg int i=1;i<=tp;i++){
		ans=addmod(ans,delmod(getf(sta[i],sta[i]),getf(sta[i]+1,sta[i])));
	}
	printf("%d\n",ans);
	return 0;
}

B. 跳跃

分析

\(st\) 表预处理出第 \(2^i\) 步后,以 \(j\) 为左端点,长度为 \(2^k\) 的区间能够向左和向右达到的最远的坐标。

二分一个答案 \(mids\) 表示是不是存在两个点需要大于 \(mids\) 步才能到达。

\(L(i,x)\)表示 \(i\)\(x\) 步的左区间边界, \(R(i,x)\) 同理。

那么就发现存在距离大于 \(mids\) 的点对 \((a,b)(a<b)\),等价于存在 \(L(b,mids)>a,R(a,mids)<b\)

维护前缀后缀最值即可快速判断。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define rg register
template<typename T>void read(rg T &x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=2e5+5;
int n,a[maxn],lg[maxn],mx[18][18][maxn],mn[18][18][maxn];
void getans(rg int id,rg int l,rg int r,rg int &x,rg int &y){
	rg int k=lg[r-l+1];
	x=std::min(mn[id][k][l],mn[id][k][r-(1<<k)+1]);
	y=std::max(mx[id][k][l],mx[id][k][r-(1<<k)+1]);
}
int mmin[maxn],mmax[maxn],fl[maxn],fr[maxn];
bool jud(rg int mids){
	rg int len,k;
	for(rg int i=1;i<=n;i++){
		len=mids,k=0;
		fl[i]=fr[i]=i;
		while(len){
			if(len&1) getans(k,fl[i],fr[i],fl[i],fr[i]);
			len>>=1,k++;
		}
	}
	mmin[0]=n+1;
	for(rg int i=1;i<=n;i++) mmin[i]=fr[i],mmax[i]=fl[i];
	for(rg int i=1;i<=n;i++) mmin[i]=std::min(mmin[i-1],mmin[i]);
	for(rg int i=n;i>=1;i--) mmax[i]=std::max(mmax[i+1],mmax[i]);
	for(rg int i=1;i<=n;i++){
		if(mmin[fl[i]-1]<i) return 1;
		if(mmax[fr[i]+1]>i) return 1;
	}
	return 0;
}
int main(){
	read(n);
	for(rg int i=1;i<=n;i++) read(a[i]);
	for(rg int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(rg int i=1;i<=n;i++){
		mx[0][0][i]=std::min(n,i+a[i]);
		mn[0][0][i]=std::max(1,i-a[i]);
	}
	for(rg int i=1;i<=17;i++){
		for(rg int j=1;j+(1<<i)-1<=n;j++){
			mn[0][i][j]=std::min(mn[0][i-1][j],mn[0][i-1][j+(1<<(i-1))]);
			mx[0][i][j]=std::max(mx[0][i-1][j],mx[0][i-1][j+(1<<(i-1))]);
		}
	}
	for(rg int j=1;j<=n;j++){
		getans(0,mn[0][0][j],mx[0][0][j],mn[1][0][j],mx[1][0][j]);
	}
	for(rg int i=1;i<=lg[n];i++){
		for(rg int j=1;j<=n;j++){
			getans(i-1,mn[i-1][0][j],mx[i-1][0][j],mn[i][0][j],mx[i][0][j]);
		}
		for(rg int j=1;j<=17;j++){
			for(rg int k=1;k+(1<<j)-1<=n;k++){
				mn[i][j][k]=std::min(mn[i][j-1][k],mn[i][j-1][k+(1<<(j-1))]);
				mx[i][j][k]=std::max(mx[i][j-1][k],mx[i][j-1][k+(1<<(j-1))]);
			}
		}
	}
	rg int l=1,r=n,mids;
	while(l<=r){
		mids=(l+r)>>1;
		if(jud(mids)) l=mids+1;
		else r=mids-1;
	}
	printf("%d\n",r+1);
	return 0;
}

C. 切蛋糕

分析

用割圆法把圆分成正 \(2000\) 边形,正常跑半平面交即可。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define rg register
const int maxn=1e5+5,maxcnt=2000;
const double pi=acos(-1),INF=1e3,eps=1e-16;
inline int dcmp(rg double now){
	return std::fabs(now)<=eps?0:(now>0?1:-1);
}
struct Node{
	double x,y;
	Node(){}
	Node(rg double aa,rg double bb){
		x=aa,y=bb;
	}
	friend Node operator +(const Node& A,const Node& B){
		return Node(A.x+B.x,A.y+B.y);
	}
	friend Node operator -(const Node& A,const Node& B){
		return Node(A.x-B.x,A.y-B.y);
	}
	friend Node operator *(const Node& A,const double& B){
		return Node(A.x*B,A.y*B);
	}
	friend Node operator /(const Node& A,const double& B){
		return Node(A.x/B,A.y/B);
	}
	friend double operator *(const Node& A,const Node& B){
		return A.x*B.x+A.y*B.y;
	}
	friend double operator ^(const Node& A,const Node& B){
		return A.x*B.y-B.x*A.y;
	}
	double angle(){
		return atan2(y,x);
	}
}q2[maxn],jl[maxn];
struct Line{
	Node s,t;
	int id;
	double ang;
	Line(rg Node aa=Node(),rg Node bb=Node(),rg int cc=0){
		s=aa,t=bb,ang=(bb-aa).angle(),id=cc;
	}
	friend bool operator <(const Line& A,const Line& B){
		double r=A.ang-B.ang;
		if(dcmp(r)==0) return dcmp((A.t-A.s)^(B.t-A.s))==-1;
		return dcmp(r)==-1;
	}
	friend double operator *(const Line& A,const Line& B){
		return (A.t-A.s)^(B.t-B.s);
	}
}q1[maxn],sta[maxn];
bool judline(rg Line aa,rg Line bb){
	return dcmp(aa*bb)==0;
}
bool onright(rg Line aa,rg Node bb){
	return dcmp((aa.t-aa.s)^(bb-aa.s))==-1;
}
Node getsi(rg Line aa,rg Line bb){
	return aa.s+(aa.t-aa.s)*(((bb.t-bb.s)^(aa.s-bb.s))/(aa*bb));
}
double getarea(rg Node aa,rg Node bb){
	return sqrt((aa.x-bb.x)*(aa.x-bb.x)+(aa.y-bb.y)*(aa.y-bb.y));
}
int head,tail,tp;
double ans1,ans2;
void SI(){
	std::sort(sta+1,sta+tp+1);
	head=tail=0;
	q1[0]=sta[1];
	for(rg int i=2;i<=tp;i++){
		if(dcmp(sta[i].ang-sta[i-1].ang)!=0){
			while(head<tail && onright(sta[i],q2[tail-1])) tail--;
			while(head<tail && onright(sta[i],q2[head])) head++;
			q1[++tail]=sta[i];
			if(head<tail) q2[tail-1]=getsi(q1[tail],q1[tail-1]);
		}
	}
	while(head<tail && onright(q1[head],q2[tail-1])) tail--;
	while(head<tail && onright(q1[tail],q2[head])) head++;
	q2[tail]=getsi(q1[head],q1[tail]);
	for(rg int i=head+1;i<=tail;i++){
		if(q1[i].id==1) ans2+=getarea(q2[i],q2[i-1]);
		else ans1+=getarea(q2[i],q2[i-1]);
	}
	if(q1[head].id==1) ans2+=getarea(q2[head],q2[tail]);
	else ans1+=getarea(q2[head],q2[tail]);
}
int n,r;
int main(){
	scanf("%d%d",&n,&r);
	rg double aa,bb;
	rg int cc;
	for(rg int i=1;i<=maxcnt;i++){
		aa=cos(2.0*i*pi/maxcnt)*r;
		bb=sin(2.0*i*pi/maxcnt)*r;
		jl[i]=Node(aa,bb);
	}
	for(rg int i=1;i<maxcnt;i++) sta[++tp]=Line(jl[i],jl[i+1],1);
	sta[++tp]=Line(jl[maxcnt],jl[1],1);
	sta[++tp]=Line(Node(-INF,-INF),Node(INF,-INF),0);
	sta[++tp]=Line(Node(INF,-INF),Node(INF,INF),0);
	sta[++tp]=Line(Node(INF,INF),Node(-INF,INF),0);
	sta[++tp]=Line(Node(-INF,INF),Node(-INF,-INF),0);
	rg Node dd,ee;
	rg double tmp1,tmp2;
	for(rg int i=1;i<=n;i++){
		scanf("%d%lf",&cc,&bb);
		if(bb==r) continue;
		tmp1=1.0*cc/180*pi;
		tmp2=acos(bb/r);
		tmp2=tmp1-tmp2;
		dd=Node(cos(tmp2)*r,sin(tmp2)*r);
		ee=Node(cos(tmp1)*bb,sin(tmp1)*bb);
		sta[++tp]=Line(dd,ee);
	}
	SI();
	printf("%.10f %.10f\n",ans1,ans2);
	return 0;
}
posted @ 2021-03-14 19:07  liuchanglc  阅读(72)  评论(0编辑  收藏  举报