zoukankan      html  css  js  c++  java
  • 省选测试38

    选拔赛

    • 先给a数组和c数组从大到小排序,D[l][r]表示分配完前K个人(A组)大于等于l,后n-K个(B组)小于等于r,枚举A组的最小分数L,D[L][L]-D[L+1][L]的和即为答案,D[L][L]和D[L+1][L]直接每次用每次dp即可,总复杂度 n^4

    • 我们会发现随着c数组中数值的减小,对于A组,能匹配的人数在减小,对于B组,能匹配的人数在增多,我们可以把当前的c[i]给还未匹配的,能匹配c最少的A组人或B组人,这样预处理出c[i]能匹配的A组人数k[i],以及能匹配的B组人数z[i],再定义dp[i][j]表示前i个c,给了A组j个的方案数,就能转移了。

    • 通过讨论c[i]给A还是给B,知道这是给A/B的第几个c,也就知道转移系数(当前的c[i]的贡献)了,自己理解的不太清楚,


    Code
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    
    const int mod=1e9+7;
    int n,K,tc,ans;
    int a[105],c[105],T[10005],k[105],z[105],f[105][105];
    
    int D(int L,int R){
    	memset(k,0,sizeof k);
    	memset(z,0,sizeof z);
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=K;++j) if(c[i]+a[j]>=L) ++k[i]; else break;
    		for(int j=n;j>=K+1;--j) if(c[i]+a[j]<=R) ++z[i]; else break;
    	}
    	f[0][0]=1;
    	for(int i=1;i<=n;++i){
    		for(int j=0;j<=i&&j<=K;++j){
    			int A=j,B=i-j;
    			f[i][j]=0;
    			if(A&&k[i]>K-A) (f[i][j]+=(ll)f[i-1][j-1]*(k[i]-(K-A))%mod)%=mod;
    			if(B&&z[i]>B-1) (f[i][j]+=(ll)f[i-1][j]*(z[i]-(B-1))%mod)%=mod;
    		}
    	}
    	return f[n][K];
    }
    
    int main(){
    	freopen("select.in","r",stdin);
    	freopen("select.out","w",stdout);
    	scanf("%d%d",&n,&K);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	for(int i=1;i<=n;++i) scanf("%d",&c[i]);
    	sort(a+1,a+1+n,greater<int>());
    	sort(c+1,c+1+n,greater<int>());
    	for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) T[++tc]=a[i]+c[j];
    	sort(T+1,T+1+tc);
    	tc=unique(T+1,T+1+tc)-(T+1);
    	for(int i=1;i<=tc;++i) (ans+=D(T[i],T[i])-D(T[i]+1,T[i]))%=mod;
    	printf("%d
    ",(ans%mod+mod)%mod);
    	return 0;
    }
    

    跳跃

    类似于ST表的预处理,lef[i][j]表示 i 跳 2^j 步能到达的左边界,rig[i][j]同理

    (lef[i][j]=min_{lef[i][j-1] leq x leq rig[i][j-1]} lef[x][j-1];)

    (rig[i][j]=max_{lef[i][j-1] leq x leq rig[i][j-1]} rig[x][j-1];)

    预处理的时候因为要查询区间min,max所以要用ST表,其实后面倍增的时候也要查询,所以这个ST表多开一维,后面再用就不要需要预处理了,

    倍增就是类似于倍增lca跳父亲,枚举当前二进制位,看答案能否加上,如果答案加上该2的次幂,仍存在两个点相互到达不了的,那么就加上,最后输出ans+1即可,这样不用二分,二分的话总复杂度 (O(nlog^2+nlog^2)) ,这样倍增的话优秀一点,复杂度 (O(nlog^2+nlog))

    Code
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int maxn=2e5+10;
    int n,ans,lg[maxn];
    int Max[maxn],lal[maxn],lar[maxn],vl[maxn],vr[maxn],lef[20][maxn],rig[20][maxn];
    int mn[20][20][maxn],mx[20][20][maxn];
    
    int read(int x=0,bool f=0,char ch=getchar()){
    	for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
    	return f?-x:x;
    }
    
    void Pre(int d){
    	for(int i=1;i<=lg[n];++i){
    		for(int j=1;j+(1<<i)-1<=n;++j){
    			mn[d][i][j]=min(mn[d][i-1][j],mn[d][i-1][j+(1<<(i-1))]);
    			mx[d][i][j]=max(mx[d][i-1][j],mx[d][i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    
    int askmin(int x,int l,int r){
    	int d=lg[r-l+1];
    	return min(mn[x][d][l],mn[x][d][r+1-(1<<d)]);
    }
    
    int askmax(int x,int l,int r){
    	int d=lg[r-l+1];
    	return max(mx[x][d][l],mx[x][d][r+1-(1<<d)]);
    }
    
    int main(){
    	freopen("jump.in","r",stdin);
    	freopen("jump.out","w",stdout);
    	n=read();
    	for(int i=2;i<=n;++i) lg[i]=lg[i/2]+1;
    	for(int i=1;i<=n;++i){
    		int x=read();
    		mn[0][0][i]=lef[0][i]=max(1,i-x);
    		mx[0][0][i]=rig[0][i]=min(n,i+x);
    	}
    	for(int d=1;d<=lg[n];++d){
    		Pre(d-1);
    		for(int i=1;i<=n;++i){
    			mn[d][0][i]=lef[d][i]=askmin(d-1,lef[d-1][i],rig[d-1][i]);
    			mx[d][0][i]=rig[d][i]=askmax(d-1,lef[d-1][i],rig[d-1][i]);
    		}
    	}
    	Pre(lg[n]);
    	Max[n+1]=1;
    	for(int i=1;i<=n;++i) lal[i]=lar[i]=i;
    	for(int i=lg[n];i>=0;--i){
    		for(int x=1;x<=n;++x){
    			vl[x]=askmin(i,lal[x],lar[x]);
    			vr[x]=askmax(i,lal[x],lar[x]);
    		}
    		for(int x=n;x>=1;--x) Max[x]=max(Max[x+1],vl[x]);
    		bool Add=0;
    		for(int x=1;x<=n;++x) if(vr[x]<n&&Max[vr[x]+1]>x){Add=1;break;}
    		if(Add){
    			ans|=(1<<i);
    			for(int x=1;x<=n;++x) lal[x]=vl[x],lar[x]=vr[x];
    		}
    	}
    	printf("%d
    ",ans+1);
    	return 0;
    }
    

    切蛋糕

    计算几何

    Code
    #include <cmath>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define double long double
    using namespace std;
    
    const int maxn=30;
    const double eps=1e-8;
    const double pi=M_PI;
    
    struct Node{
    	double x,y;
    	Node operator - (const Node &B) const {return (Node){x-B.x,y-B.y};}
    	Node operator + (const Node &B) const {return (Node){x+B.x,y+B.y};}
    	Node operator * (const double &B) const {return (Node){x*B,y*B};}
    	double operator ^ (const Node &B) const {return x*B.y-B.x*y;};
    	double operator ~ () const {return sqrt(x*x+y*y);}
    	bool operator < (const Node &B) const {
    		double at1=atan2(y,x),at2=atan2(B.y,B.x);
    		return at1!=at2?at1<at2:x<B.x;
    	}
    };
    struct Line{
    	Node st,ed;
    	bool operator & (const Line &B) const {//跨立实验,判线段相交
    		return ((B.ed-st)^(ed-st))*((B.st-st)^(ed-st))<0&&((ed-B.st)^(B.ed-B.st))*((st-B.st)^(B.ed-B.st))<0;
    	}
    	Node operator * (const Line &B) const {//线段交点
    		Node x=ed-st,y=B.ed-B.st,z=B.st-st;
    		return st+x*((y^z)/(y^x));
    	}
    };
    struct Sim{
    	Node x;
    	bool opt;
    	bool operator < (const Sim &B) const {return x<B.x;}
    };
    vector <Sim> vec;
    
    int read(int x=0,bool f=0,char ch=getchar()){
    	for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
    	return f?-x:x;
    }
    
    int main(){
    	freopen("cut.in","r",stdin);
    	freopen("cut.out","w",stdout);
    	int n=read(),R=read(),top=2; vec.resize(3);
    	vec[1]=(Sim){(Node){-1.0l*R,0},1};
    	vec[0]=vec[2]=(Sim){(Node){1.0l*R,0},1};
    	Node O=(Node){0,0};
    	while(n-->0){
    		double ang=read()*pi/180,h; scanf("%LF",&h);
    		if(fabs(h-R)<=eps) continue;
    		Node s=(Node){cos(ang-acos(h/R))*R,sin(ang-acos(h/R))*R};//和圆的一交点
    		Node t=(Node){cos(ang+acos(h/R))*R,sin(ang+acos(h/R))*R};//和圆的另一交点
    		Node tmp1,tmp2;int p1=0,p2=0;
    		for(int i=1;i<=top;++i){
    			if(vec[i].opt){
    				if(((s-vec[i-1].x)^(vec[i].x-vec[i-1].x))>0){
    					if(!p1) p1=i,tmp1=s;
    					else p2=i,tmp2=s;
    				}
    				if(((t-vec[i-1].x)^(vec[i].x-vec[i-1].x))>0){
    					if(!p1) p1=i,tmp1=t;
    					else p2=i,tmp2=t;
    				}
    			}
    			else if((Line){vec[i-1].x,vec[i].x}&(Line){s,t}){
    				if(!p1) p1=i,tmp1=(Line){vec[i-1].x,vec[i].x}*(Line){s,t};
    				else p2=i,tmp2=(Line){vec[i-1].x,vec[i].x}*(Line){s,t};
    			}
    		}
    		if(!p2) continue;
    		if(((O-tmp1)^(tmp2-tmp1))<0){
    			bool opt=vec[p1].opt;
    			top-=p2-p1;
    			for(int i=p2-1;i>=p1;--i) vec.erase(vec.begin()+i);
    			vec.push_back((Sim){tmp1,opt});
    			vec.push_back((Sim){tmp2,0});
    			top+=2;
    		}
    		else{
    			bool opt=vec[p2].opt;
    			for(int i=top;i>=p2;--i) vec.erase(vec.begin()+i);
    			for(int i=p1-1;i>=1;--i) vec.erase(vec.begin()+i);
    			top=p2-p1;
    			vec.push_back((Sim){tmp1,0});
    			vec.push_back((Sim){tmp2,opt});
    			top+=2;
    		}
    		sort(vec.begin()+1,vec.begin()+top+1),vec[0]=vec[top];
    	}
    	double ans1=0,ans2=0;
    	for(int i=1;i<=top;++i){
    		if(vec[i].opt){
    			double tmp=atan2(vec[i].x.y,vec[i].x.x)-atan2(vec[i-1].x.y,vec[i-1].x.x);
    			if(tmp<0) tmp+=2*pi;
    			ans2+=tmp*R;
    		}
    		else ans1+=(~(vec[i].x-vec[i-1].x));
    	}
    	printf("%.10LF %.10LF
    ",ans1,ans2);
    	return 0;
    }
    
  • 相关阅读:
    HDU1029 Ignatius and the Princess IV
    UVA11039 Building designing【排序】
    UVA11039 Building designing【排序】
    POJ3278 HDU2717 Catch That Cow
    POJ3278 HDU2717 Catch That Cow
    POJ1338 Ugly Numbers(解法二)
    POJ1338 Ugly Numbers(解法二)
    UVA532 Dungeon Master
    UVA532 Dungeon Master
    POJ1915 Knight Moves
  • 原文地址:https://www.cnblogs.com/Lour688/p/14539184.html
Copyright © 2011-2022 走看看