• AtCoder Grand Contest 021题解


    传送门

    (A)

    咕咕

    ll n,res;bool fl;
    int main(){
    	scanf("%lld",&n),fl=1;
    	while(n>9)res+=9,fl&=(n%10==9),n/=10;
    	printf("%lld
    ",res+n-1+fl);
    	return 0;
    }
    

    (B)

    只有凸包上的点有贡献,且把以这个点为端点的两条凸包上的线的中垂线画出来,它的概率就是两条中垂线的夹角除以(2pi)

    //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=105;const double Pi=acos(-1.0);
    struct pt{
    	int x,y;double ang;
    	inline pt(){}
    	inline pt(R int xx,R int yy):x(xx),y(yy){}
    	inline pt operator -(const pt &b)const{return pt(x-b.x,y-b.y);}
    	inline void calc(){ang=atan2(y,x);}
    }p[N],st[N];
    double ans[N],pw;int n;
    void solve(R int id){
    	R int top=0;
    	fp(i,1,n)if(i!=id)st[++top]=p[i]-p[id],st[top].calc();
    	sort(st+1,st+1+top,[](const pt &a,const pt &b){return a.ang<b.ang;});
    	R double mx=0;
    	st[top+1].ang=st[1].ang+2*Pi;
    	fp(i,1,top)cmax(mx,st[i+1].ang-st[i].ang);
    	if(mx<=Pi)return;
    	ans[id]=mx-Pi;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d",&n);
    	if(n==2)return printf("0.5
    0.5
    "),0;
    	fp(i,1,n)scanf("%d%d",&p[i].x,&p[i].y);
    	fp(i,1,n)solve(i);
    	pw=1.0/(2*Pi);
    	fp(i,1,n)printf("%.10lf
    ",ans[i]*pw);
    	return 0;
    }
    

    (C)

    直接划分成若干个(2 imes 2)的格子填,然后如果多出来一行或者一列,那么就优先把这一行一列给填满

    不过有一个边界情况,比方说(3 imes 3)一共填(2)个<>和(2)个^v,那么可以是

    <>^
    ^*v
    v<>

    所以这意味着我们填多余行列的时候要尽量往右上填

    //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=1005;
    char s[N][N];int n,m,a,b,c,d;
    int main(){
    	scanf("%d%d%d%d",&n,&m,&a,&b),c=n,d=m;
    	if(n*m<(a+b)*2)return puts("NO"),0;
    	fp(i,1,n)fp(j,1,m)s[i][j]='.';
    	if(m&1){
    		R int p=1;
    		while(b&&p+1<=n){
    			s[p][m]='^',s[p+1][m]='v';
    			p+=2,--b;
    		}
    		--m;
    	}
    	if(n&1){
    		R int p=d;
    		while(a&&p-1>=1){
    			s[n][p]='>',s[n][p-1]='<';
    			p-=2,--a;
    		}
    		--n;
    	}
    	R int rem=n*m;
    	for(R int i=1;i<=n;i+=2)for(R int j=m-1;j>=1;j-=2){
    		if(a>=2){
    			a-=2,rem-=4;
    			s[i][j]=s[i+1][j]='<',
    			s[i][j+1]=s[i+1][j+1]='>';
    		}else if(b>=2){
    			b-=2,rem-=4;
    			s[i][j]=s[i][j+1]='^',
    			s[i+1][j]=s[i+1][j+1]='v';
    		}
    	}
    	if(!a||!b||rem>=8){
    		for(R int i=1;i<=n;i+=2)for(R int j=m-1;j>=1;j-=2)
    			if(s[i][j]=='.'){
    				if(a)--a,s[i][j]='<',s[i][j+1]='>';
    				else if(b)--b,s[i][j]='^',s[i+1][j]='v';
    			}
    	}else if(rem>=4){
    		if(a==1&&b==1&&(c&1)&&(d&1)){
    			--a,--b;
    			s[n-1][1]='<',s[n-1][2]='>',
    			s[n][1]='^',s[n+1][1]='v';
    		}
    	}
    	if(a||b)return puts("NO"),0;
    	puts("YES");
    	fp(i,1,c){
    		fp(j,1,d)putchar(s[i][j]);
    		putchar('
    ');
    	}
    	return 0;
    }
    

    (D)

    等价于求最长回文子序列,设(f[l][r][k])表示考虑到([l,r])这个区间,还剩(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;
    const int N=305;
    char s[N];int f[N][N][N],n,k;
    inline int max(R int x,R int y){return x>y?x:y;}
    int dfs(int l,int r,int k){
    	if(r<=l)return r-l+1;
    	if(~f[l][r][k])return f[l][r][k];
    	R int res=max(dfs(l+1,r,k),dfs(l,r-1,k));
    	if(s[l]==s[r])cmax(res,dfs(l+1,r-1,k)+2);
    	if(s[l]!=s[r]&&k)cmax(res,dfs(l+1,r-1,k-1)+2);
    	return f[l][r][k]=res;
    }
    int main(){
    	scanf("%s%d",s+1,&k),n=strlen(s+1);
    	memset(f,-1,sizeof(f));
    	printf("%d
    ",dfs(1,n,k));
    	return 0;
    }
    

    (E)

    首先显然得满足红球个数大于等于蓝球且红球个数大于等于(n)

    我们枚举放红球的个数(a)和蓝球的个数(b),考虑喂史莱姆的最优策略,将史莱姆编号为(1)(n),对于红球,如果在(1)(n-1)中有史莱姆没喂到过红球,那么随便选一个喂,对于蓝球,如果在(1)(n-1)中有一个喂过红球且没喂过蓝球,那么就喂它。对于其它的球全部扔给(n)号史莱姆。不难发现这样一定是最优的

    那么来考虑什么样的序列合法,记红球为(1),蓝球为(-1),设(t=a-(n-1)),也就是喂给(n)的红球个数。如果(t>b),那么任何序列都合法。否则,序列合法当且仅当任意一个前缀和(geq -(t-1))(也就意味着(n)号史莱姆吃的蓝球-红球个数永远不能大于等于(t),否则就(gg)了),序列个数类似于卡特兰数一样算一下就行了

    记得特判一下(a=b)的情况,此时整个序列的最后一个必然是蓝球,所以我们只需要考虑前(k-1)个数就行了(因为没有特判调了一个下午)

    //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;
    }
    const int N=1e6+5;
    int fac[N],ifac[N],n,k,res;
    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 calc(R int n,R int m,R int k){return dec(C(n+m,n),C(n+m,n+1-k));}
    void init(int n=1e6){
    	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);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&k);
    	if(k<n)return puts("0"),0;
    	init();
    	for(R int a=max((k+1)>>1,n),b;a<=k;++a){
    		b=k-a;
    		R int t=min(a-(n-1),b);
    		if(a-(n-1)==t)--t;
    		upd(res,calc(a,b-(a==b),-t));
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (F)

    (jz)姐姐好生教育了一番

    首先考虑(dp),设(f[i][j])表示考虑完了(i)(j)列,且这(i)行全都不为空的方案数,转移的时候枚举(k),表示本行第一个数在第(j+1)列的行数,如果(k=0),那么系数乘上({i(i+1)over 2}+1)(前(i)行随便选,考虑这一列的黑格子最大最小值的方案数),如果(k>0),那么系数就是({i+k+2choose k+2})(这(k)行可以和前(i)行交换顺序,然后还有两个本列的黑格子最大最小值的名额,那么把这两个名额当做插板加进去算组合数)。这样我们就得到了一个(O(n^2m))(dp)

    发现转移过程中所有系数都只和(i)有关,且相当于每一次从(j)转移到(j+1),那么(NTT)优化就行了

    //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;
    }
    const int N=(1<<14)+5,M=205;
    int r[N],rt[2][N<<1],lim,d,ilim;
    void NTT(int *A,int ty){
        fp(i,0,lim-1)if(i<r[i])swap(A[i],A[r[i]]);
        for(R int mid=1;mid<lim;mid<<=1)
            for(R int j=0,t;j<lim;j+=(mid<<1))
                fp(k,0,mid-1)
                    A[j+k+mid]=dec(A[j+k],t=mul(rt[ty][mid+k],A[j+k+mid])),
                    A[j+k]=add(A[j+k],t);
        if(!ty)fp(i,0,lim-1)A[i]=mul(A[i],ilim);
    }
    int coef[N],fac[N],ifac[N],f[N],g[N],n,m,res;
    inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
    void init(int n=10000){
        fp(i,1,(1<<d)-1)r[i]=(r[i>>1]>>1)|((i&1)<<(d-1));
        for(R int t=(P-1)>>1,i=1,x,y;i<lim;i<<=1,t>>=1){
            x=ksm(3,t),y=ksm(332748118,t),rt[0][i]=rt[1][i]=1;
            fp(k,1,i-1)
                rt[1][i+k]=mul(rt[1][i+k-1],x),
                rt[0][i+k]=mul(rt[0][i+k-1],y);
        }
    	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);
    	coef[0]=0;fp(i,1,lim>>1)coef[i]=ifac[i+2];
    	NTT(coef,1);
    }
    void calc(){
    	fp(i,0,n)g[i]=mul(f[i],ifac[i]);fp(i,n+1,lim-1)g[i]=0;
    	NTT(g,1);
    	fp(i,0,lim-1)g[i]=mul(g[i],coef[i]);
    	NTT(g,0);
    	fp(i,0,n)f[i]=add(mul(g[i],fac[i+2]),mul(f[i],(i*(i+1)>>1)+1));
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	lim=1,d=0;while(lim<=(n<<1))lim<<=1,++d;
    	ilim=ksm(lim,P-2);
    	init();
    	f[0]=1;
    	fp(i,1,m)calc();
    	fp(i,0,n)upd(res,mul(C(n,i),f[i]));
    	printf("%d
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    git commit 合并
    git 管理 Linux 文件系统
    python 全局变量的使用
    JavaScript 中 类型转换
    canconfig 配置命令
    python 调用 shell 命令
    python 3 操作mysql数据库的方法
    python 字符串和整数,浮点型互相转换
    JavaScript 里面的整数 位 操作
    JavaScript 使用 php 的变量
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11687321.html
Copyright © 2020-2023  润新知