• CODE FESTIVAL 2016 Grand Final 题解


    传送门

    越学觉得自己越蠢……这场除了(A)之外一道都不会……

    (A)

    贪心从左往右扫,能匹配就匹配就好了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=2e5+5;
    struct node{
    	int x,op;
    	inline bool operator <(const node &b)const{return x<b.x;}
    }a[N];int n,res,ca,cb;
    int main(){
    	scanf("%d",&n),res=1;
    	fp(i,1,n)scanf("%d",&a[i].x),a[i].op=0;
    	fp(i,1,n)scanf("%d",&a[i+n].x),a[i+n].op=1;
    	sort(a+1,a+1+(n<<1));
    	fp(i,1,n<<1)if(a[i].op==0){
    		if(!cb)++ca;
    		else res=mul(res,cb--);
    	}else{
    		if(!ca)++cb;
    		else res=mul(res,ca--);
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (B)

    二分答案,如果以最长的边作为(c),那么两个圆的圆心肯定是在(A,B)的角平分线上是最优的,判断一下此时会不会超出边界就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const double pi=acos(-1.0);
    double x[5],y[5],A,B,a,b,c,l,r,mid;
    inline double sqr(R double x){return x*x;}
    inline double calc(R int i,R int j){
    	return sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));
    }
    inline bool ck(R double x){return x+x+x*A+x*B<=c;}
    int main(){
    	fp(i,1,3)scanf("%lf%lf",&x[i],&y[i]);
    	a=calc(1,2),b=calc(1,3),c=calc(2,3);
    	if(a>c)swap(a,c);if(b>c)swap(b,c);
    	A=0.5*acos((a*a+c*c-b*b)/(2*a*c));
    	B=0.5*acos((b*b+c*c-a*a)/(2*b*c));
    	A=1.0/tan(A),B=1.0/tan(B);
    	l=0,r=1000;
    	while(r-l>1e-11){
    		mid=(l+r)*0.5;
    		ck(mid)?l=mid:r=mid;
    	}
    	printf("%.12lf
    ",l);
    	return 0;
    }
    

    (C)

    首先发现,(a_ioplus a_{i-1})的结果必定形如(2^k-1)

    那么我们记当前的(oplus)结果为(res),然后从(29)位依次考虑到第(0)位,如果当前位为(1)且存在某一个数字满足(a_ioplus a_{i-1}=2^{k+1}-1),那么就异或上这个数。最终如果(res)不为(0)就说明无解了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=5e5+5;
    int a[N],cnt[25],n,res,sum;
    inline int calc(R int x){R int res=0;while(x)x>>=1,++res;return res;}
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]),res^=a[i],++cnt[calc(a[i]^(a[i]-1))];
    	fd(i,29,0)if(res>>i&1)
    		if(cnt[i+1])res^=(1<<(i+1))-1,++sum;
    	printf("%d
    ",res?-1:sum);
    	return 0;
    }
    

    (D)

    好迷啊……

    (A)的最优策略肯定是选一个数(x),并且以(x)的概率选红色,(1-x)的概率选蓝色,那么(B)获胜的机会就是(max(p_1x,q_1(1-x))+max(p_2x,q_2(1-x))+...)

    容易发现这个函数一定有最低点,那么三分就好了

    具体证明去看原题解吧……

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    double p[15],q[15],l,r,s;
    inline double max(R double x,R double y){return x>y?x:y;}
    inline double min(R double x,R double y){return x<y?x:y;}
    int main(){
    	s=1.0/100;
    	fp(i,1,6)scanf("%lf",&p[i]),p[i]*=s;
    	fp(i,1,6)scanf("%lf",&q[i]),q[i]*=s;
    	l=0,r=1,s=1.0/3;
    	for(R int T=233;T;--T){
    		R double l1=l+(r-l)*s,r1=r-(r-l)*s;
    		R double a1=0,a2=0;
    		fp(i,1,6){
    			a1+=max(l1*p[i],(1-l1)*q[i]);
    			a2+=max(r1*p[i],(1-r1)*q[i]);
    		}
    		a1>a2?l=l1:r=r1;
    	}
    	R double res=0;
    	fp(i,1,6)res+=max(r*p[i],(1-r)*q[i]);
    	printf("%.10lf
    ",res);
    	return 0;
    }
    

    (E)

    神仙的转化……

    首先我们考虑,如果(i,j)之间有运水的关系,那么我们就在(i,j)之间连一条边,这样的话图会构成若干个连通块,我们对于每个联通块单独考虑

    (A)为连通块中所有点的水量之和,(B)为连通块中所有边的权值之和,(n)为点数,那么这个连通块中最大的最小值是({A-Bover n}),且容易证明可以达到这个上界

    那么我们对于每一个连通块(O(2^nn^2log n))求出最小生成树,然后(O(3^n))(dp)就可以了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int N=25,M=(1<<15)+5;
    double mn[M];int x[N],y[N],a[N],st[N],top,n,lim;
    struct eg{
    	int u,v;double w;
    	inline bool operator <(const eg &b)const{return w<b.w;}
    }E[N*N];int fa[N],tot;
    inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline double calc(R int i,R int j){
    	return sqrt(1ll*(x[i]-x[j])*(x[i]-x[j])+1ll*(y[i]-y[j])*(y[i]-y[j]));
    }
    void solve(R int s){
    	top=tot=0;fp(i,0,n-1)if(s>>i&1)st[++top]=i,mn[s]+=a[i];
    //	if(!top)printf("%d %d
    ",s,top);
    	fp(i,1,top)fa[i]=i;
    	fp(i,1,top)fp(j,i+1,top)E[++tot]={i,j,calc(st[i],st[j])};
    	sort(E+1,E+1+tot);
    	for(R int i=1,u,v;i<=tot;++i){
    		u=find(E[i].u),v=find(E[i].v);
    		if(u!=v)mn[s]-=E[i].w,fa[u]=v;
    	}
    	mn[s]/=top;
    }
    int main(){
    	scanf("%d",&n),lim=(1<<n);
    	fp(i,0,n-1)scanf("%d%d%d",&x[i],&y[i],&a[i]);
    	fp(i,1,lim-1)solve(i);
    	mn[0]=1e18;
    	fp(s,1,lim-1)for(R int t=(s-1)&s;t;t=(t-1)&s)cmax(mn[s],min(mn[t],mn[s^t]));
    	printf("%.10lf
    ",mn[lim-1]);
    	return 0;
    }
    

    (F)

    这题似乎并没有那么难啊……可我还是没有想出来……

    首先为了方便起见,如果(n)是偶数,我们加入一个([0,0])使其变成奇数,并设(m={n-1over 2})

    不难发现移动之后处于最中间的那个区间位置是不会变的,同时如果把中间那个区间左边的分别记为(p_1,p_2,...,p_m),右边的分别记为(q_1,q_2,...,q_m),那么对于一个左边的区间(p_i),它需要左移的距离为(r_{p_i}+sum_{j=1}^{i-1}len_{p_j}),其中(len_i)表示第(i)个区间的长度,右边也同理

    如果展开来的话,就是

    [egin{aligned} ans &=sum_{i=1}^m l_{p_i}+sum_{i=1}^m r_{q_i}+sum_{i=1}^m(i-1)len_{p_i}+sum_{i=1}^m(i-1)len_{q_i}+m imes len_o end{aligned} ]

    那么很显然应该按从大到小来放置(p,q),直接(dp)就可以了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=5005;
    ll f[2][N][2];int l[N],r[N],id[N],n,t,m;
    inline bool cmp(const int &x,const int &y){return l[x]+r[x]>l[y]+r[y];}
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d%d",&l[i],&r[i]);
    	n+=(n&1^1),m=(n-1)>>1;fp(i,1,n)id[i]=i;
    	sort(id+1,id+1+n,cmp);
    	memset(f,0x3f,sizeof(f));
    	f[0][0][0]=0,t=0;
    	for(R int i=0;i<=m;++i,t^=1){
    		memset(f[t^1],0x3f,sizeof(f[t^1]));
    		fp(j,0,m)fp(k,0,1){
    			R int ip=id[i+j+k+1],len=l[ip]+r[ip];
    			if(i<m)cmin(f[t^1][j][k],f[t][j][k]+r[ip]+1ll*len*i);
    			if(j<m)cmin(f[t][j+1][k],f[t][j][k]+l[ip]+1ll*len*j);
    			if(k<1)cmin(f[t][j][k+1],f[t][j][k]+1ll*m*len);
    		}
    	}
    	printf("%lld
    ",f[t^1][m][1]);
    	return 0;
    }
    

    (G)

    把最终的串看成每个(FESTIVA)后面接若干个(L)

    考虑(c_i)表示第(i)次出现的(FESTIVA)后面连续的(L)的个数,记(f_i)为前面这(i)个数会产生的(FESTIVA)的子序列的个数

    (f_i)很好计算,等价于令(sum a_i=7),隔板法计算即可

    最后只要把(k)(f_i)进制分解即可

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef long long ll;
    const int N=505;
    ll f[N],k;int c[N];
    inline ll calc(R int x){
    	R ll res=1;
    	fp(i,x,x+6)res*=i;
    	return res/5040;
    }
    int main(){
    	scanf("%lld",&k);
    	fp(i,1,500)f[i]=calc(i);
    	fd(i,500,1)if(k>=f[i])c[i]=k/f[i],k%=f[i];
    	fp(i,1,500){
    		printf("FESTIVA");
    		fp(k,1,c[i])putchar('L');
    	}
    	return 0;
    }
    

    (H)

    话说这好像是那道集训队作业的原题……而且我抄的那份代码似乎有点眼熟……

    首先,对于秩相同的(C),答案是一样的,因为如果我们对(C)做线性变换,我们可以通过对(A)(B)做同样的线性变换使得等式仍然成立

    (C)的秩为(r)

    根据矩乘的定义可知,设(A_i)表示(A)的列向量,(C_i)表示(C)的列向量,则(C_i)必定在(A_i)生成的线性空间中

    (x)表示(A)的秩,那么(B)相当于是一个线性方程组,即(B)对应的方案有(2^{n(n-x)})

    然后再来考虑(A),设(f[i][j])表示一个(n imes i)的矩阵,且矩阵的秩为(j)的方案数,那么对于所有秩为(i)的矩阵(A),对答案的贡献就是(f[n][i] imes 2^{n(n-i)} imes f[i][r])

    最后除以秩为(r)的矩阵个数就行了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=305;
    bitset<N>c,bas[N];int f[N][N],bin[N*N],vis[N],res,r,n,x;
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n){
    		fp(j,1,n)scanf("%d",&x),c[j]=x;
    		fd(j,n,1)if(c[j]){
    			if(!vis[j]){
    				bas[j]=c,vis[j]=1,++r;
    				break;
    			}
    			c^=bas[j];
    		}
    	}
    	bin[0]=1;fp(i,1,n*n)bin[i]=mul(bin[i-1],2);
    	f[0][0]=1;
    	fp(i,0,n-1)fp(j,0,n)if(f[i][j]){
    		upd(f[i+1][j],mul(f[i][j],bin[j]));
    		upd(f[i+1][j+1],mul(f[i][j],dec(bin[n],bin[j])));
    	}
    	fp(i,r,n)upd(res,1ll*f[n][i]*bin[n*(n-i)]%P*f[i][r]%P);
    	res=mul(res,ksm(f[n][r],P-2));
    	printf("%d
    ",res);
    	return 0;
    }
    

    (I)

    代码算是长的了……抄的时候会有很多的细节

    首先,如果把(270)看成(-90),那么所有角的和加起来必须是(360)否则无解

    如果此时为(n=4)且四个数为({90,90,90,90}),随便找一组解

    否则一定能循环移位之后使得(a_2=90,a_3=270),我们递归找出一组({a_1,a_4,a_5,..,a_n})的解(q),考虑如何构造一组合法的解(p)

    (p_1=p_2=p_3=q_1,p_i=q_{i-2}(igeq 4))

    (p_2,p_3)向右平移(0.5)个单位,让(p_3,p_4)向上平移(0.5)个单位即可

    具体实现和一些细节建议看代码理解

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb emplace_back
    #define fi first
    #define se second
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    typedef pair<int,int> pi;
    vector<pi> solve(vector<int> s){
    	R int n=s.size();
    	if(n==4){
    		vector<pi>ans(4);
    		ans[0]=pi(1,0),ans[1]=pi(1,1),ans[2]=pi(0,1),ans[3]=pi(0,0);
    		return ans;
    	}
    	R int pos=-1;
    	fp(i,0,n-1)if(s[i]==90&&s[(i+1)%n]==270){pos=i;break;}
    	vector<int>st;
    	fp(i,pos-1,pos-1+n-1)if(i!=pos&&i!=pos+1)st.pb(s[(i+n)%n]);
    	vector<pi>pans=solve(st),ans(n);
    	fp(i,0,n-3){
    		R pi p=pans[i];
    		if(!i||p.fi>pans[0].fi)++p.fi;
    		if(p.se>pans[0].se)++p.se;
    		R int to=(!i?0:i+2);
    		ans[to]=p;
    	}
    	ans[1]=pi(ans[0].fi,ans[0].se+1),
    	ans[2]=pi(ans[1].fi-1,ans[1].se);
    	rotate(ans.begin(),ans.begin()+(n-pos+1)%n,ans.end());
    	if(ans[0].fi!=ans[1].fi){
    		fp(i,0,n-1)swap(ans[i].fi,ans[i].se),ans[i].fi*=-1;
    	}
    	if(ans[0].se>ans[1].se){
    		fp(i,0,n-1)ans[i].fi*=-1,ans[i].se*=-1;
    	}
    	return ans;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	int n,sum=0;
    	scanf("%d",&n);
    	vector<int>p(n);
    	fp(i,0,n-1)scanf("%d",&p[i]),sum+=(p[i]==90?1:-1);
    	if(sum!=4)return puts("-1"),0;
    	vector<pi>ans=solve(p);
    	fp(i,0,n-1)ans[i].fi+=5e8,ans[i].se+=5e8;
    	fp(i,0,n-1)printf("%d %d
    ",ans[i].fi,ans[i].se);
    	return 0;
    }
    

    (J)

    首先我们把在同一组的连边,对于(x,x+1),如果没有一条边跨过这两个数,我们称左右两边的数不同组

    然后对于同一组我们考虑,发现间隔只会有四种可能

    那么我们枚举(31)(333)的个数,然后用组合数算一下就可以了

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    const int P=1e9+7;
    inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
    inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
    inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
    inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
    int ksm(R int x,R int y){
    	R int res=1;
    	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    	return res;
    }
    const int N=10005;
    int fac[N],ifac[N];
    void init(int n=1e4){
    	fac[0]=ifac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
    	ifac[n]=ksm(fac[n],P-2);fd(i,n-1,1)ifac[i]=mul(ifac[i+1],i+1);
    }
    inline int C(R int n,R int m){return m<0||m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
    inline int ch(R int n,R int m){return n==0&&m==0?1:C(n+m-1,m-1);}
    int n,a,b,c,res;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d%d%d",&n,&a,&b,&c),res=0;
    	init();
    	if(b&1)return puts("0"),0;
    	fp(i,0,a)fp(j,0,(c-i)/3){
    		R int n1=i,n2=a-i,n3=j,n4=b>>1,k=c-i-j*3;
    		if (n1<0||n2<0||n3<0||n4<0||k<0)continue;
    		R int now=mul(fac[n1+n2+n3+n4],ch(k,n4));
    		now=1ll*now*ifac[n1]%P*ifac[n2]%P*ifac[n3]%P*ifac[n4]%P;
    		upd(res,now);
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    Apache日志分析
    iptables日志探秘
    php与其他一些相关工具的安装步骤分享
    ERROR 1 (HY000): Can't create/write to file '/tmp/#sql_830_0.MYI' (Errcode: 13)
    一些可能需要的正则
    restful api的简单理解
    认识MySQL Replication
    如何处理缓存失效、缓存穿透、缓存并发等问题
    经典算法mark
    php常用的一些代码
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11508861.html
Copyright © 2020-2023  润新知