zoukankan      html  css  js  c++  java
  • 省选测试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
    ",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
    ",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
    ",ans1,ans2);
    	return 0;
    }
    
  • 相关阅读:
    微信小程序真机预览跟本地不同的问题。原文地址:https://blog.csdn.net/qq_27187991/article/details/69664247/
    java 获取目标时间到当前时间中间的月份和每月最大时间
    关于事物与性能的测试
    两步搞定一台电脑同时开启多个tomcat
    一个java新手配置IIS服务器的血泪史
    16、指数加权平均算法介绍及偏差修正
    15、优化算法之Mini-batch 梯度下降法
    14、改善深层神经网络之梯度检验
    13、改善深层神经网路之权重初始化方法
    12、改善深层神经网络之梯度消失与梯度爆炸及其具体解决方案
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14533658.html
Copyright © 2011-2022 走看看