• AtCoder Grand Contest 020 题解


    传送门

    怎么又是(tourist)神仙的题……

    (A)

    咕咕

    int n,a,b;
    int main(){
    	scanf("%d%d%d",&n,&a,&b);
    	puts(((b-a-1)&1)?"Alice":"Borys");
    	return 0;
    }
    

    (B)

    考虑从后往前做,假设考虑到(a_i),且只考虑第(a_{i+1})(a_n)的答案为(s),那么考虑了(a_i)的答案(p)要满足(p/a_i imes a_i=s)(这里是下取整),因为这里必有(a_i|s),所以(p)需要满足(s+a_i-1geq pgeq s),那么我们就可以把(p)的范围给求出来了,这样一直求到(a_1)就可以了,注意还要保证(p)恒为(a_{p-1})的倍数,所以还需要处理一下范围

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define pb emplace_back
    #define gg return puts("-1"),0
    #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=1e5+5;
    int a[N],n;ll mn,mx,l,r;
    inline int dec(R int x,R int y){return y?x-y:0;}
    int main(){
    	scanf("%d",&n);
    	fp(i,1,n)scanf("%d",&a[i]);
    	mx=mn=2,a[0]=1;
    	if(2%a[n])gg;
    	for(R int i=n;i;--i){
    		l=mn,r=mx;
    		mn=(l+a[i-1]-1)/a[i-1]*a[i-1];
    		mx=(r+a[i]-1)/a[i-1]*a[i-1];
    		if(mn>mx)gg;
    	}
    	printf("%lld %lld
    ",mn,mx);
    	return 0;
    }
    

    (C)

    考虑把空集也算上的话中位数是(p={sum a_iover 2})(因为对于每一种取法,它的补集的和与它的和必然是(sum a_i)),那么去掉空集的话中位数就是(p)的后继了

    //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=2005,M=4e6+5;
    bitset<M>vis;int a[N],n,sum,res;
    int main(){
    	scanf("%d",&n);
    	vis[0]=1;
    	fp(i,1,n)scanf("%d",&a[i]),sum+=a[i],vis|=vis<<a[i];
    	res=(sum+1)>>1;
    	fp(i,res,sum)if(vis[i])return printf("%d
    ",i),0;
    	puts("QAQ");
    	return 0;
    } 
    

    (D)

    自己想想觉得很麻烦,不过似乎并没有啊……

    首先,最小长度(k={(max(a,b))over min(a,b)+1})(上取整),就是考虑把其中一个字母塞到另外一个字母的中间

    如果(k=1),随便搞,否则的话考虑贪心

    对于当前字母的方法,需要满足以下两个条件,一个是连续长度不能超过(k+1),另一个是后面的字母产生的也不会超过(k+1)

    那么不难发现前缀一段一定是(AA..ABAA..AB...)就是(A)重复(k)遍之后后面接一个(B)

    那么假设现在还剩(a)(A)(b)(B),那么当前位置可以当(A)当且仅当(bleq ka),因为最坏的情况是一个(A)后接(k)(B),而且不难发现后缀一段一定都是形如这种情况

    所以我们只需要找到前缀和后缀的分界点,然后就可以(O(1))计算每一个位置的值了。注意最优情况下分界点处必定是一个(A),且这个(A)是前后缀共用的,假设分界点及其之前有(nb)(B)(na)(A),那么有(nbleq max(0,lfloor{{na-1over k} floor})),同时也有(na=a-lceil{{b-nbover k} ceil}+1),带入之后有(nbleq lfloor{{a-lceil{{b-nbover k} ceil}over k} floor}),发现当(nb)增大时,左边递增,右边递减,那么二分找到最大的(nb)即可

    然后没有然后了

    //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 char s[2]={'A','B'};
    int a,b,c,d,q,k,nb;ll lim;
    inline int calc(R int x){return (a-(b-x+k-1)/k)/k;}
    inline int find(){
    	R int l=0,r=b,mid,ans=0;
    	while(l<=r){
    		mid=(l+r)>>1;
    		mid<=calc(mid)?(ans=mid,l=mid+1):r=mid-1;
    	}
    	return ans;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	for(scanf("%d",&q);q;--q){
    		scanf("%d%d%d%d",&a,&b,&c,&d),--c;
    		k=(a+b)/(a<b?a+1:b+1);
    		if(k==1)fp(i,c,d-1)putchar(s[(i+(b>a))&1]);
    		else{
    			nb=find();
    //			printf("qwq %d %d %d %d
    ",a,b,k,nb);
    			lim=nb+a-(b-nb+k-1)/k+1;
    			fp(i,c,d-1)putchar(i<lim?s[i%(k+1)==k]:s[(a+b-i)%(k+1)>0]);
    		}
    		putchar('
    ');
    	}
    	return 0;
    } 
    

    (E)

    首先设(f(s))表示串(s)的压缩方案,那么只要考虑(s)的开头字母是单独出现还是被压缩了即可

    类似的我们可以得到对于子集求和的算法,即设(f(s))表示对(s)这个串的子集和合法压缩方案,被压缩的情况和之前一样,只不过枚举压缩的长度和次数的时候需要把这几段取一个按位与,如果如果开头字母单独出现且开头字母是(1)需要乘上(2)(因为此时子集中开头可以是(1)也可以是(0)

    这个爆搜其实是可以过的……因为不同的串(s)的数量只有大约(50000)个……

    证明什么的并没有看懂……

    //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=998244353;
    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;
    }
    map<string,int>dp;
    inline string operator &(const string &a,const string &b){
    	R int n=a.length();string c(n,'0');
    	fp(i,0,n-1)c[i]=(a[i]=='1'&&b[i]=='1'?'1':'0');
    	return c;
    }
    int dfs(string s){
    	if(s.empty())return 1;
    	if(dp.count(s))return dp[s];
    	if(s.length()==1)return s[0]-'0'+1;
    	R int res=mul(s[0]=='1'?2:1,dfs(s.substr(1)));
    	R int n=s.length();
    	fp(d,1,n){
    		string t=s.substr(0,d);
    		for(R int k=d;k<=n-d;k+=d){
    			t=t&s.substr(k,d);
    			upd(res,mul(dfs(t),dfs(s.substr(k+d))));
    		}
    	}
    	return dp[s]=res;
    }
    int main(){
    	string s;cin>>s;
    	printf("%d
    ",dfs(s));
    	return 0;
    }
    

    (F)

    首先默认长度最长的那一条弧放在([0,x])的位置上,然后考虑其它弧的左端点的位置

    有一个套路,我们把左端点位置的整数部分和小数部分分开考虑,那么整数部分只有(n)种可能,而小数部分虽然可能性有无数种,但是它们之间的相对顺序只有(n!)种(默认互不相同,因为连续型随机变量取值相同的概率可以视为(0)),而且只有这个相对顺序会对答案有影响

    那么我们(n!)爆搜小数部分的相对顺序,把圆上给拆成(n imes c)个点,其中第((i,j))个点表示整数部分为(i),小数部分为(j),然后设(f[i][j][k])表示现在考虑到点(i),当前最远能到点(j),已经选了的点的集合为(k)的方案数,转移即可

    //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;
    int h[15],a[15],vis[15],n,c,lim;
    ll f[2][305][(1<<5)+5];double ans,pw;
    void solve(){
    	memset(f,0,sizeof(f));
    	R int t=0;
    	f[t][min(c*n,a[n]*n)][0]=1;
    	fp(i,1,c*n-1)if(i%n){
    		memset(f[t^1],0,sizeof(f[t^1]));
    		R int to=i%n-1;
    		fp(j,i,c*n)fp(k,0,lim-1)if(f[t][j][k]){
    			f[t^1][j][k]+=f[t][j][k];
    			if(k>>to&1^1)f[t^1][min(c*n,max(j,i+a[h[to+1]]*n))][k^(1<<to)]+=f[t][j][k];
    		}
    		t^=1;
    	}
    	ans+=f[t][c*n][lim-1];
    }
    void dfs(int dep){
    	if(dep>=n)return solve();
    	fp(i,1,n-1)if(!vis[i]){
    		h[dep]=i,vis[i]=1;
    		dfs(dep+1);
    		vis[i]=0;
    	}
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&c);
    	fp(i,1,n)scanf("%d",&a[i]);
    	sort(a+1,a+1+n),lim=(1<<(n-1));
    	dfs(1);
    	pw=1;fp(i,1,n-1)pw*=c;
    	ans*=1.0/pw;
    	pw=1;fp(i,2,n-1)pw*=i;
    	ans*=1.0/pw;
    	printf("%.12lf
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    XML验证框架在项目中的应用
    Container.DataItem几种方式.
    XMLSpy 的使用
    介绍一个工具给大家,做网站时,经常要上传文件到外网服务器,但是上传时往往需要很长时间,如果有一个文件对比工具……
    Xcopy 帮助.net 2005组件化开发
    不影响原有的onload方法的前提下,在页面中增加onload的执行方法
    如何将XSD文件以及引入import的文件生成相应的C#类。
    封装my97时间控件成asp.net 时间控件,支持多语言,皮肤,时间大小限制,时间格式验证功能,非常强大。
    参数化使用ADO.NET的OleDb方法时注意不能使用@参数
    提供一个通用的Javascript验证页面输入的脚本给大家,并希望大家提意见呀
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11673579.html
Copyright © 2020-2023  润新知