zoukankan      html  css  js  c++  java
  • Code Chef May Challenge 2019题解

    传送门

    (REDONE)

    贡献可以拆成(X(Y+1)+Y),那么一个数(x)的贡献对最终答案的贡献就是(x(a_1+1)(a_2+1)...),那么最终答案肯定是(sumlimits_{i=1}^niprodlimits_{j=1}^{i-1}(j+1))最优

    void init(){
    	fac[0]=1;
    	fp(i,1,1e6)fac[i]=mul(fac[i-1],i+1),ans[i]=add(ans[i-1],mul(i,fac[i-1]));
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	init();
    	for(int T=read();T;--T)n=read(),print(ans[n]);
    	return Ot(),0;
    }
    

    (MATCHS)

    直接辗转相除,假设(n<m)(如果(n=m)先手必胜),如果(leftlfloor{mover n} ight floor=1)先后手必胜反一反,否则先手必胜,用(SG)函数就能证明

    ll n,m;int flag;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	for(int T=read();T;--T){
    		n=read(),m=read(),flag=1;
    		if(n>m)swap(n,m);
    		while(m%n&&m/n==1)flag^=1,m%=n,swap(n,m);
    		puts(flag?"Ari":"Rich");
    	}
    	return 0;
    }
    

    (WTBTR)

    先把坐标系旋转(45)度再扩大(sqrt{2})倍,那么只能选竖直或者水平的直线

    易知最优方案一定是(n-2)条直线穿过(n-2)个点,剩下一条直线在剩下两个点的中间位置,所以只要求出平面最近点对就行了(这里的距离只有(x)(y)的距离)。正确性可以用反证法证明。

    int main(){
    //	freopen("testdata.in","r",stdin);
    	for(int T=read();T;--T){
    		n=read(),ans=inf;
    		for(R int i=1,x,y;i<=n;++i)x=read(),y=read(),a[i]=x-y,b[i]=x+y;
    		sort(a+1,a+1+n),sort(b+1,b+1+n);
    		fp(i,2,n)cmin(ans,a[i]-a[i-1]),cmin(ans,b[i]-b[i-1]);
    		printf("%lf
    ",ans*0.5);
    	}
    	return 0;
    }
    

    (ADAROKS2)

    劲啊……

    考虑一种涂法,我们不断涂副对角线,并保证涂的过程中没有矩形

    第一条副对角线从((1,1))开始,然后涂((2,2),(3,3))……第二条副对角线从((1,2))开始,第三条从((1,4))开始……

    所以我们该如何保证涂副对角线的过程中没有矩形呢?

    我们令副对角线之间的距离依次增大,并记这个距离为(a_i),那么有(a_1=1,a_2=2,...),不难发现如果存在矩形就意味着有(sumlimits_{i=l}^ra_i=sumlimits_{j=ll}^{rr}a_i),且([l,r]cap[ll,rr]=varnothing)

    可以直接暴力把(a_i)预处理出来,然后后面染副对角线就行了

    有个比较尴尬的问题就是如果(nleq 150)左右黑色格子数会少于(8n),因为左下角那个三角形是没有染的,我们暴力染一下

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #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;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=1e5+5;
    int pos[N],n,tot;char mp[1005][1005];
    bool ck(int x){
    	pos[tot+1]=pos[tot]+x;
    	fp(i,1,tot)fp(j,i,tot)fp(k,i+1,tot+1)if(pos[tot+1]-pos[k-1]==pos[j]-pos[i-1])return false;
    	return true;
    }
    void init(){
    	for(R int i=1;pos[tot]<2000;++i)
    		if(ck(i))pos[++tot]=i,pos[tot]+=pos[tot-1];
    	fd(i,tot,1)pos[i]-=pos[i-1];
    }
    bool cck(R int i,R int j){
    	fp(k,1,n)fp(l,1,n)if(i!=k&&j!=l)
    		if((mp[k][j]=='O')+(mp[i][l]=='O')+(mp[k][l]=='O')==3)return false;
    	return true;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	init();
    	int res;
    	for(int T=read();T;--T){
    		n=read(),res=0;
    		fp(i,1,n)fp(j,1,n)mp[i][j]='.';
    		for(R int i=1,j=1;i<=n;i+=pos[j++])
    			for(R int x=1,y=i;y<=n;++x,++y)mp[x][y]='O',++res;
    		for(R int i=1;i<=n&&res<8*n;++i)
    			for(R int j=1;j<=i&&res<8*n;++j)
    				if(mp[i][j]!='O'&&cck(i,j))mp[i][j]='O',++res;
    		fp(i,1,n){
    			fp(j,1,n)putchar(mp[i][j]);
    			putchar('
    ');
    		}
    	}
    	return 0;
    }
    

    (BINARY)

    我们可以看做是(1)不断往左跳,那么我们只要知道每个(1)往左跳了多少次就可以了,要注意不能跳过界

    所以现在问题就是该如何计算一个(1)在这(z)秒里有多少秒是往左跳的,有多少秒是停下来的

    考虑每一个极长的(1)的联通块,对于开头的那个(1),它是第(1)秒就开始跳了的,对于第(i)(1),它是从第(i)秒才开始向左跳的。如果我们用一个队列来表示一个跳和停地状态,(q[i]=0)表示停下,(q[i]=1)表示跳跃,那么这个极长联通块中没加入一个(1)都会令(q[++t]=0)并使(++h),只要保证(t-h+1=z),动态维护这个区间的和就好了

    然而跳跃的情况该如何考虑呢?设这个极长联通块到前一个极长联通块之间的(0)的个数为(p),那么以当前的极长联通块中第一个点为例,它会连续往左跳(p)秒,此时有可能前一个联通块中所有点都已经跳完了,那么它可以继续跳。否则它就必须停下来等一下之后才能继续跳。如果以我们之前的那个(q)为例,可以发现一次往后加了(0)点的时候,会把(q)中的最后一个(1)变成(0)。那么我们只要对(1)开一个链表,记录一下上一个(1)的位置就行了

    然后没有然后了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #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;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    inline int getop(){R char ch;while((ch=getc())>'9'||ch<'0');return ch-'0';}
    char sr[1<<22];int K=-1;
    inline void Ot(){fwrite(sr,1,K+1,stdout),K=-1;}
    const int N=1e6+5;
    int a[N],b[N],q[N<<1],las[N<<1],n,z,h,t,tl,now,tot;
    int main(){
    //	freopen("testdata.in","r",stdin);
    	for(int T=read();T;--T){
    		n=read(),z=read();
    		fp(i,1,n)a[i]=getop(),b[i]=0;
    		tl=t=now=tot=0,h=1;fp(i,1,z)q[++t]=1,++now;
    		fp(i,1,n){
    			if(a[i]==1)b[max(++tot,i-now)]=1,q[++t]=0,las[t]=tl,tl=t,now-=q[h++];
    			else if(tl>=h)q[tl]=1,++now,tl=las[tl];
    		}
    		fp(i,1,n)sr[++K]=b[i]+'0',sr[++K]=' ';
    		sr[++K]='
    ';
    	}
    	return Ot(),0;
    }
    

    (TREDEG)

    强行二合一……

    首先我们把无根树转化为(prufer)序列,那么无根树的总数就是(n^{n-2}),只要计算总的贡献就行了。对于(nleq 100000)的数据答案就是

    [Ans=[x^{n-2}]left(sum_{i=0}^infty {(d_i+1)^kover d_i!}x^i ight)^n ]

    直接多项式快速幂就行了

    然后是(k=1)的情况,此时(nleq 2000000),直接多项式快速幂显然会爆炸

    那就换个方法数吧

    [egin{aligned} Ans &=(n-2)!prod_{sum d_i=n-2}left({d_i+1over d_i!} ight)\ &=(n-2)!prod_{sum d_i=n-2}left({1over d_i!}+{1over (d_i-1)!} ight)\ end{aligned} ]

    我们把括号里的给拆出来,那么假设有(x)({1over (d_i-1)!})(n-x)({1over d_i!}),那么(sum d_i=n-2-x),且我们枚举这(n)个里面那些是(d_i-1),方案数还需要乘上一个({nover x})

    于是总的方案数为

    [egin{aligned} Ans &=(n-2)!sum_{k=0}^{n-2}{nchoose k}prod_{sum d_i=n-2-k}{1over d_i!}\ &=sum_{k=0}^{n-2}{nchoose k}(n-2)^{underline{k}}(n-2-k)!prod_{sum d_i=n-2-k}{1over d_i!}\ &=sum_{k=0}^{n-2}{nchoose k}(n-2)^{underline{k}}n^{n-2-k}\ end{aligned} ]

    直接(O(n))计算就行了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #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;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=(1<<18)+5,P=998244353,M=2e6+5;
    inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
    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;
    }
    int lg[N],r[21][N],rt[2][N],lim,d;
    int fac[M],ifac[M],inv[M];
    void init(){
    	fp(d,1,18){
    		fp(i,1,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
    		lg[1<<d]=d;
    	}
    	fac[0]=inv[0]=ifac[0]=fac[1]=inv[1]=ifac[1]=1;
    	fp(i,2,2e6){
    		fac[i]=mul(fac[i-1],i),
    		inv[i]=mul(P-P/i,inv[P%i]),
    		ifac[i]=mul(ifac[i-1],inv[i]);
    	}
    	for(R int t=(P-1)>>1,i=1,x,y;i<262144;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);
    	}
    }
    void NTT(int *A,int ty){
    	fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
    	R int t;
    	for(R int mid=1;mid<lim;mid<<=1)
    		for(R int j=0;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){
    		t=inv[lim];
    		fp(i,0,lim-1)A[i]=mul(A[i],t);
    	}
    }
    void Inv(int *a,int *b,int len){
    	if(len==1)return b[0]=ksm(a[0],P-2),void();
    	Inv(a,b,len>>1);static int A[N],B[N];
    	lim=(len<<1),d=lg[lim];
    	fp(i,0,len-1)A[i]=a[i],B[i]=b[i];
    	fp(i,len,lim-1)A[i]=B[i]=0;
    	NTT(A,1),NTT(B,1);
    	fp(i,0,lim-1)A[i]=mul(A[i],mul(B[i],B[i]));
    	NTT(A,0);
    	fp(i,0,len-1)b[i]=dec(add(b[i],b[i]),A[i]);
    	fp(i,len,lim-1)b[i]=0;
    }
    void Ln(int *a,int *b,int len){
    	static int A[N],B[N];
    	fp(i,1,len-1)A[i-1]=mul(a[i],i);A[len-1]=0;
    	Inv(a,B,len);lim=(len<<1),d=lg[lim];
    	fp(i,len,lim-1)A[i]=B[i]=0;
    	NTT(A,1),NTT(B,1);
    	fp(i,0,lim-1)A[i]=mul(A[i],B[i]);
    	NTT(A,0);
    	fp(i,1,len-1)b[i]=mul(A[i-1],inv[i]);b[0]=0;
    	fp(i,len,lim-1)b[i]=0;
    }
    void Exp(int *a,int *b,int len){
    	if(len==1)return b[0]=1,void();
    	Exp(a,b,len>>1);
    	static int A[N];Ln(b,A,len);
    	lim=(len<<1),d=lg[lim];
    	A[0]=dec(a[0]+1,A[0]);
    	fp(i,1,len-1)A[i]=dec(a[i],A[i]);
    	fp(i,len,lim-1)A[i]=b[i]=0;
    	NTT(A,1),NTT(b,1);
    	fp(i,0,lim-1)b[i]=mul(b[i],A[i]);
    	NTT(b,0);
    	fp(i,len,lim-1)b[i]=0;
    }
    void ksm(int *a,int *b,int len,int k){
    	static int A[N];
    	Ln(a,A,len);
    	fp(i,0,len-1)A[i]=mul(A[i],k);
    	Exp(A,b,len);
    }
    int n,k,ans;
    inline int C(R int n,R int m){return 1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
    int dfs(int pos,int now){
    	if(pos==n+1)return now==n-2?1:0;
    	int res=0;
    	fp(i,0,n-2-now)res=add(res,mul(dfs(pos+1,now+i),mul(1,ifac[i])));
    	return res;
    }
    int calc1(){
    	int res=0;
    	int iv=ksm(n,P-2);
    	for(R int i=0,p=ksm(n,n-2),tmp=n-2;i<=n-2;p=mul(p,mul(iv,tmp--)),++i)
    		res=add(res,1ll*fac[n]*ifac[i]%P*ifac[n-i]%P*p%P);
    	return res;
    }
    int A[N],B[N];
    int calc2(){
    	int len=1;while(len<=n-2)len<<=1;
    	fp(i,0,len-1)A[i]=mul(ksm(i+1,k),ifac[i]);
    	ksm(A,B,len,n);
    	return mul(B[n-2],fac[n-2]);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	init();
    	for(int T=read();T;--T){
    		n=read(),k=read();
    		ans=(k==1)?calc1():calc2();
    		ans=mul(ans,ksm(ksm(n,n-2),P-2));
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    (ADAPWN)

    我们可以把一次操作看成删除一个点,那么这道题就可以看做是一个二分图,选出最少的点删掉,使得剩下的点之间没有边相连,也就是一个最小点覆盖

    不过这里有一个问题,就是有一些点是不可能被删掉的,所以它们是不能被选在最小点覆盖里的。我们强制它们不删,那么所有和它们有边相连的点都要被强制删掉

    然后就是如何求二分图最小点覆盖了……鉴于咱对二分图一无所知,可以看看(fcw)巨巨的总结

    没了

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define inline __inline__ __attribute__((always_inline))
    #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;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    int read(char *s){
    	R int len=0;R char ch;while((ch=getc())!='O'&&ch!='.');
    	for(s[++len]=ch;(ch=getc())=='O'||ch=='.';s[++len]=ch);
    	return s[len+1]='',len;
    }
    const int N=205,M=2e4+5;
    struct eg{int v,nx;}e[M];int head[M<<2],tot;
    inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
    char mp[N][N];int id[N][N],px[M],py[M],to[M],vis[M],bl[M],now[M],ok[M];
    int n,cnt,tim,res;
    bool dfs(int u){
    	vis[u]=tim;
    	go(u)if(vis[v]!=tim){
    		vis[v]=tim;
    		if(!to[v]||dfs(to[v]))return to[v]=u,true;
    	}
    	return false;
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	for(int T=read();T;--T){
    		n=read(),cnt=tot=res=0;
    		fp(i,1,n){
    			read(mp[i]);
    			fp(j,1,n)if(mp[i][j]=='O')id[i][j]=++cnt,px[cnt]=i,py[cnt]=j,bl[cnt]=(i&1);
    		}
    		fp(i,1,n+1)mp[n+1][i]=0;
    		fp(i,1,cnt)to[i]=vis[i]=head[i]=now[i]=ok[i]=0;
    		fp(i,1,n)fp(j,1,n)if(mp[i][j]=='O'&&mp[i-1][j-1]!='O'&&mp[i-1][j+1]!='O'){
    			now[id[i][j]]=1;
    			if(mp[i+1][j+1]=='O')now[id[i+1][j+1]]=2;
    			if(mp[i+1][j-1]=='O')now[id[i+1][j-1]]=2;
    		}
    		fp(i,1,cnt)res+=(now[i]==2);
    		for(R int i=1;i<=n;i+=2)fp(j,1,n)if(mp[i][j]=='O'&&!now[id[i][j]]){
    			if(mp[i-1][j-1]=='O'&&!now[id[i-1][j-1]])add(id[i][j],id[i-1][j-1]);
    			if(mp[i-1][j+1]=='O'&&!now[id[i-1][j+1]])add(id[i][j],id[i-1][j+1]);
    			if(mp[i+1][j-1]=='O'&&!now[id[i+1][j-1]])add(id[i][j],id[i+1][j-1]);
    			if(mp[i+1][j+1]=='O'&&!now[id[i+1][j+1]])add(id[i][j],id[i+1][j+1]);
    		}
    		tim=0;
    		fp(i,1,cnt)if(!now[i]){
    			++tim;
    			if(dfs(i))++res;
    		}
    		fp(i,1,cnt)if(to[i])ok[to[i]]=1;
    		++tim;
    		fp(i,1,cnt)if(!now[i]&&bl[i]&&!ok[i])dfs(i);
    		fp(i,1,cnt)if(!now[i]&&bl[i]&&vis[i]!=tim)now[i]=2;
    		fp(i,1,cnt)if(!now[i]&&!bl[i]&&vis[i]==tim)now[i]=2;
    		printf("%d
    ",res);
    		fd(i,cnt,1)if(now[i]==2){
    			int x=px[i],y=py[i];
    			printf("%d %d %c
    ",x,y,mp[x-1][y-1]=='O'?'L':'R');
    		}
    	}
    	return 0;
    }
    

    剩下的慢慢补……

  • 相关阅读:
    css sprites图片背景优化技术
    CSS3新特性(整理贴)
    span HTML元素
    jQuery1.2选择器(2)
    JavaScript 弹出窗口总结
    jQuery1.2选择器(1)
    jquery如何判断checkbox(复选框)是否被选中
    使用 Lightbox 2 和 JavaScript 构建出色的图片库
    如何在一台电脑同时安装IE6、IE7、FF2、FF3进行网页调试
    IE的有条件注释详解(附实例代码)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10808881.html
Copyright © 2011-2022 走看看