zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 019 题解

    传送门

    (A)

    咕咕

    int a,b,c,d,n,t;
    int main(){
    	scanf("%d%d%d%d%d",&a,&b,&c,&d,&n);
    	t=n&1,n-=t,a*=8,b*=4,c*=2;
    	printf("%lld
    ",1ll*min(min(a,b),min(c,d))*(n>>1)+(min(min(a,b),c)>>1)*t);
    	return 0;
    }
    

    (B)

    先不考虑结果不变的操作,发现如果一个翻转的区间([l,r])满足(s[l]=s[r])那么我们现在可以选择操作([l+1,r-1])而使得最终的序列和翻转([l,r])之后的序列相等,那么我们就不计数([l,r]),这样我们只要数出有多少对([l,r])满足(s[l]\neq s[r]),最后答案(+1)即可

    typedef long long ll;
    const int N=2e5+5;
    char s[N];int cnt[26],n,sum;ll res;
    int main(){
    	scanf("%s",s+1),n=strlen(s+1);
    	fp(i,1,n)res+=sum-cnt[s[i]-'a'],++cnt[s[i]-'a'],++sum;
    	printf("%lld
    ",res+1);
    	return 0;
    }
    

    (C)

    直接把那个矩阵中的点拿出来,然后按纵坐标从小到大排序,以(x)做一个最长上升子序列,如果长度(k < min(x_2 - x_1,y_2 - y_1) + 1),那么可以拐角(k)次,减少((20-5pi)k)

    否则只能拐角(k-1)次然后再走一个半圆,减少((20-5pi)(k-1))再加上((10pi -20))

    //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=2e5+5;const double Pi=acos(-1.0);
    int x[N],y[N],id[N],f[N],num[N];
    int n,tot,cnt;
    inline bool cmp(const int &a,const int &b){return y[a]<y[b];}
    int main(){
    	int x1,y1,x2,y2;
    	scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&n);
    	fp(i,1,n)scanf("%d%d",&x[i],&y[i]);
    	if(x1>x2)swap(x1,x2),swap(y1,y2);
    	if(y1>y2){
    		swap(y1,y2);
    		fp(i,1,n)y[i]=y1+y2-y[i];
    	}
    	fp(i,1,n)id[i]=i;
    	sort(id+1,id+1+n,cmp);
    	fp(i,1,n){
    		R int u=id[i];
    		if(y[u]>=y1&&y[u]<=y2&&x[u]>=x1&&x[u]<=x2)
    			num[++tot]=x[u];
    	}
    	cnt=0,f[cnt]=-1;
    	fp(i,1,tot){
    		if(num[i]>f[cnt])f[++cnt]=num[i];
    		else{
    			R int k=lower_bound(f+1,f+1+cnt,num[i])-f;
    			f[k]=num[i];
    		}
    	}
    	R double res;
    	if(cnt<min(x2-x1,y2-y1)+1)
    		res=100.0*(x2-x1+y2-y1)-(20-Pi*5)*cnt;
    	else res=100.0*(x2-x1+y2-y1)-(20-Pi*5)*(cnt-1)+(10*Pi-20);
    	printf("%.12lf
    ",res);
    	return 0;
    }
    

    (D)

    首先枚举(A)中的那一个位置最终和(B_1)匹配,然后对于每一个位置如果和最终的位置不等,那么判一下在过程中是否会碰到(1),如果是的话可以在那里变成

    否则我们对于每一个无法达到的点求出额外向左(/)右移动的最小距离,如果令一个左边最远的走左边,那么剩下的也都可以走左边,否则这个左边最远的必须走右边。同理次远也是一样

    总的复杂度(O(n^2log n))

    //quming
    #include<bits/stdc++.h>
    #define R register
    #define fi first
    #define se second
    #define pb emplace_back
    #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;
    const int N=2005,inf=0x3f3f3f3f;
    char s[N*3],s1[N];
    int sum[N*3],nl[N*3],nr[N*3],res,len;
    vector<pi>vc;
    void init(){
    	nl[0]=-inf;
    	fp(i,1,len*3)nl[i]=(s[i]=='1'?i:nl[i-1]);
    	nr[len*3+1]=inf;
    	fd(i,len*3,1)nr[i]=(s[i]=='1'?i:nr[i+1]);
    	fp(i,1,len*3)sum[i]=sum[i-1]+(s[i]=='1');
    }
    inline int findl(R int x){return min(inf,x-nl[x]);}
    inline int findr(R int x){return min(inf,nr[x]-x);}
    int main(){
    	scanf("%s%s",s1+1,s+1),res=inf;
    	len=strlen(s+1);
    	fp(i,len+1,len*3)s[i]=s[i-len];
    	init();
    	fp(i,1,len*2+1){
    		vc.clear();
    		R int c=0;
    		fp(j,1,len)if(s1[j]!=s[i+j-1]){
    			++c;
    			R int r=j+len,l=j+i-1;
    			if(l>r)swap(l,r);
    			if(sum[r]-sum[l-1]==0)
    			vc.pb(pi(findl(l),findr(r)));
    		}
    		vc.pb(pi(0,0));
    		sort(vc.begin(),vc.end());
    		reverse(vc.begin(),vc.end());
    		R int tmp=inf,mx=0;
    		for(auto p:vc){
    			if(p.fi!=inf&&mx!=inf)cmin(tmp,mx*2+p.fi*2);
    			cmax(mx,p.se);
    			if(mx==inf)break;
    		}
    		if(tmp!=inf)cmin(res,tmp+abs(len+1-i)+c);
    	}
    	if(res==inf)res=-1;
    	printf("%d
    ",res);
    	return 0;
    }
    

    (E)

    想到这些方法的都是什么神仙啊……

    首先有用的位置只有两个,两个全(1)或者一个(1)一个(0),方便起见把全(1)记做中间点,一个(1)一个(0)记做左点,一个(0)一个(1)记做右点。记中间点个数为(A),左点和右点的个数都是(B)

    我们先匹配再考虑顺序,对于一个((a_i,b_i))的匹配,把(a_i)(b_i)连一条边,那么最终的图肯定是若干个环加若干条链,环上都是中间点,链上开头和结尾分别是左右点,中间都是中间点。对于环,上面操作顺序任意,对于链,操作顺序必须固定

    sol1

    (f[i][j])表示把(j)个中间点放到(i)条链中的方案数,则(f[i][j]=sum _{kgeq 0}{f[i-1][j-k]over (k+1)!}),分母表示链上(k+1)条边的顺序固定,那么最终的答案就是(A!B!(A+B)!sum_{i=0}^A f[B][i]),其中((A+B)!)表示边随便排列,而(A!)(B!)代表两种点之间有标号

    然后我们每一次转移相等于乘上一个多项式(sum_{igeq 0}{1over (i+1)!}x^i),那么多项式快速幂就行了,复杂度(O(nlog n))

    sol2

    另一种方法是设(f[i][j])表示一共放了(i)个中间点和(j)个左点,则

    [egin{aligned} f[i][j]=i imes j imes f[i-1][j]+j^2 imes f[i][j-1] end{aligned} ]

    前一个是表示插入一个中间点,它的标号任意,即可以随便和之前一个交换标号,所以乘(i),然后可以插在任意一条链的后面,所以乘(j)

    后一个是表示插入一个新的左点,同理标号任意所以乘(j),又因为可以选择任意一个右点所以再乘(j)

    复杂度(O(n^2))已经可以通过了

    不过还可以用多项式优化,设(g[i][j]={f[i][j]over (j!)^2(i!)}),则有(g[i][j]=g[i][j-1]+j imes g[i-1][j])

    原来的可以看成是一个网格从左上出发到右下,每次往下走就乘上(ij),往右走就乘上(j^2),求所有路径权值和,变成(g[i][j])之后就是只有往下走会乘上(j)

    那么我们枚举在第(i)列走了几步,发现第(i)列的生成函数就是({1over 1-ix}),答案就是所有列的生成函数之积,直接分治(NTT+)多项式求逆即可,复杂度(O(nlog^2 n))

    因为比较懒所以只写了(O(n^2))

    //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=10005;
    char s[N],t[N];int fac[N],ifac[N],f[N][N],n,cnta,cntb;
    inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
    inline void init(int n=10000){
    	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(){
    	init();
    	scanf("%s%s",s+1,t+1),n=strlen(s+1);
    	fp(i,1,n){
    		cnta+=(s[i]=='1'&&t[i]=='1');
    		cntb+=(s[i]=='1'&&t[i]=='0');
    	}
    	f[0][0]=1;
    	fp(j,1,cntb)f[0][j]=mul(fac[j],fac[j]);
    	fp(i,1,cnta)fp(j,1,cntb)
    		f[i][j]=(1ll*i*j%P*f[i-1][j]+1ll*j*j%P*f[i][j-1])%P;
    	R int res=0;
    	fp(i,0,cnta){
    		R int j=cnta-i;
    		upd(res,1ll*f[i][cntb]*fac[j]%P*fac[j]%P*C(cnta,j)%P*C(cnta+cntb,j)%P);
    	}
    	printf("%d
    ",res);
    	return 0;
    }
    

    (F)

    好秒……

    首先假设现在还剩(n)(Yes)(m)(No),那么肯定是选择较多的那个回答,如果一样的话随便蒙一个

    然后我们把这个东西放到一个坐标系里,那么一种回答的方案相当于是从((n,m))出发到((0,0)),如果以(y=x)把坐标系分成两部分,则在这条线下面往左走会有贡献,在上面往下走会有贡献

    先假设(n=m),且走的过程中没有碰到过对角线,那么不管在上面走还是在下面走贡献都是(n)

    否则先假设(ngeq m),那么在走到对角线之前,一定会先往左走(n-m)步,如果到了对角线,我们以每次碰到对角线为界把路径分成若干部分,那么每一部分的贡献都是之前说的没有碰到过对角线的贡献了,所以总的贡献必定是(n),同理当(mgeq n)的时候贡献一定是(m)

    然后是对角线上的点,不管往哪边走贡献都是({1over 2}),那么我们对于每个点判断一下经过它的概率是多少即可

    //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,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;}
    inline int calc(R int n,R int m){return C(n+m,n);}
    inline 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(){
    	init();
    	scanf("%d%d",&n,&m);
    	fp(i,1,min(n,m))upd(res,mul(calc(i,i),calc(n-i,m-i)));
    	res=mul(res,ksm(mul(calc(n,m),2),P-2));
    	upd(res,max(n,m));
    	printf("%d
    ",res);
    	return 0;
    }
    
  • 相关阅读:
    350. Intersection of Two Arrays II
    94. Binary Tree Inorder Traversal
    623. Add One Row to Tree
    JS判断是否为数字,中文,小写、大写字母
    ASP.NET 操作Cookie详解 增加,修改,删除
    ASP.NET MVC 入门1、简介
    通过LINQ TO SQL类显示数据库表的数据
    OutputCache缓存优化asp.net代码 提高网页性能
    数据库读取二进制图片显示到PictureBox中
    WinForm窗体间如何传值的几种方法
  • 原文地址:https://www.cnblogs.com/yuanquming/p/11600267.html
Copyright © 2011-2022 走看看